π’ Docker In-Depth — Build, Ship & Run Your Apps Everywhere (A Practical Guide) π
π’ Docker In-Depth — Build, Ship & Run Your Apps Everywhere (A Practical Guide) π
Containers changed how we build and run software. This post walks you through what Docker is, how it works under the hood, core components, a step-by-step migration of a sample app to Docker (with real Dockerfile and docker-compose
examples), use cases, features, best practices, and a handy cheat-sheet. Ready? Let’s sail. ⛵️

π What is Docker? (Short & Sweet)
Docker is a platform that packages an application and its dependencies into a container — a lightweight, portable, and self-contained runtime. Containers share the host OS kernel but are isolated (process, filesystem, network), making them fast to start and consistent across environments.
Benefits at a glance:
- Portability (dev → QA → prod)
- Fast startup and low overhead vs VMs
- Reproducibility and easier CI/CD
- Great for microservices, local dev, testing
π§© Core Concepts & Components
1. Image
A read-only template (layers) used to create containers. Built from a Dockerfile
. Images are immutable and layered (copy-on-write).
2. Container
A running instance of an image — isolated process(s) with its own namespace, filesystem view, and resource limits.
3. Dockerfile
A text file with instructions to build an image (e.g., FROM
, COPY
, RUN
, CMD
).
4. Docker Engine / Daemon (dockerd
)
The background service that builds, runs, and manages containers.
5. Docker CLI (docker
)
Client tool to interact with the daemon (build, run, push, etc).
6. Registry
A service to store/retrieve images (Docker Hub, GitHub Container Registry, private registry).
7. Volumes
Persistent storage for containers. Keeps data outside the writable container layer.
8. Networks
Connect containers; Docker provides bridge, host, overlay networks.
9. Compose
docker-compose
defines and runs multi-container apps (local orchestration).
10. Orchestration
For production scaling: Docker Swarm, Kubernetes. These manage scheduling, scaling, service discovery, rolling updates.
11. Under the hood
- Namespaces → isolation (PID, mount, network, UTS, IPC, user).
- cgroups → resource limits (CPU, memory, IO).
- Union file systems (overlayfs) → layered images, copy-on-write.
- runc / containerd → lower-level runtime components.
⚙️ How Docker Works (Stepwise)
- Write a Dockerfile: describes how to assemble an image.
docker build
: daemon executes instructions, producing an image (layers cached).- Image stored locally as layered filesystem. Tag it.
docker run
: daemon creates a container from the image, sets up namespaces & cgroups, mounts the filesystem, and starts the process.- Networking created (bridge by default). Ports mapped to host if requested.
- Volume mount if persistence needed.
- Push images to a registry; other hosts
docker pull
anddocker run
. - Orchestrator schedules containers across nodes, handles scaling & health.
π Step-by-Step: Move Your App to Docker (Practical Example)
We’ll containerize a simple Node.js app (the same steps work for Rails, Python, Java, etc).
Project structure
my-app/
├─ package.json
├─ index.js
├─ .dockerignore
1) Add .dockerignore
node_modules
npm-debug.log
.git
2) Dockerfile (simple + recommended multistage)
# Stage 1: build
FROM node:18 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build # if you have a build step
# Stage 2: runtime - smaller image
FROM node:18-slim
WORKDIR /app
ENV NODE_ENV=production
# Create non-root user
RUN useradd --user-group --create-home --shell /bin/false appuser
COPY --from=build /app ./
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 3000
CMD ["node", "index.js"]
Why multistage? Keeps final image small and avoids shipping build tools.
3) Build the image locally
docker build -t yourusername/my-app:1.0.0 .
4) Run locally (development)
# map port, mount source for live edit
docker run --rm -it -p 3000:3000 -v "$(pwd)":/app yourusername/my-app:1.0.0
5) Compose for multi-service (app + db)
docker-compose.yml
:
version: "3.8"
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://postgres:password@db:5432/mydb
depends_on:
- db
volumes:
- .:/app
db:
image: postgres:15
environment:
POSTGRES_DB: mydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Start:
docker-compose up --build -d
6) Tag & push to registry
docker login
docker tag yourusername/my-app:1.0.0 ghcr.io/yourusername/my-app:1.0.0 # or dockerhub
docker push ghcr.io/yourusername/my-app:1.0.0
7) Deploy to a server
On the server:
docker pull ghcr.io/yourusername/my-app:1.0.0
docker run -d --restart=unless-stopped -p 80:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=... \
--name my-app \
ghcr.io/yourusername/my-app:1.0.0
8) Production considerations
- Instead of
docker run
, use an orchestrator (Kubernetes, ECS, or Docker Swarm). - Use environment variables and secrets (do not store secrets in images).
- Use health checks (
HEALTHCHECK
in Dockerfile or readiness probe in k8s). - Logging: stdout/stderr (centralize with ELK, Grafana Loki, etc).
✅ Docker Use Cases & Features
Use cases
- Microservices architecture
- CI/CD pipelines (build/test images per commit)
- Local dev environments mirroring production
- Reproducible testing environments
- Resource-efficient multi-tenant workloads
- Edge and serverless containers
- Packaging legacy apps to standardize runtime
Notable features
- Image layering & caching for fast builds
- Multi-stage builds to reduce final image size
- Named volumes for persistent data
- Network models: bridge/overlay/host
- Service discovery (via Docker Swarm or external)
- Compose for local dev orchestration
- Integration with registries, CI (GitHub Actions, GitLab CI, Jenkins)
- Security scanning & signing (with tools like trivy, cosign)
π§ Best Practices (Do These)
- Use small base images (e.g.,
alpine
,slim
) when appropriate. - Use multistage builds to keep images minimal.
- Add
.dockerignore
to avoid copying unnecessary files (speeds build). - Pin base image versions (
node:18.16.0-slim
) to avoid surprises. - Run processes as non-root inside containers.
- Set resource limits (
--memory
,--cpus
) for production containers. - Use environment variables for config; use secrets stores for credentials.
- Use health checks:
HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1
- Scan images regularly (Trivy, Clair).
- Keep images ephemeral; store data in volumes or external services.
- Use immutable tags (release
v1.2.3
) along withlatest
for development.
π‘️ Security Tips
- Minimize the attack surface: fewer packages, smaller images.
- Use official or trusted base images.
- Regularly update and rebuild images for OS/security patches.
- Avoid embedding secrets in images or source.
- Run containers with least privileges (
--cap-drop ALL
, read-only filesystem). - Scan images and sign trusted images for provenance.
π§ Troubleshooting — Quick Tips
- Container exits immediately: check
docker logs <container>
anddocker ps -a
. - Port conflict: ensure host port not already in use.
- Permission error on volumes: fix ownership/uid mapping or use named volumes.
- Slow builds: leverage caching (ordering of Dockerfile), use specific
.dockerignore
. - Large image size: inspect layers (
docker history
) and use multistage.
π Useful Commands Cheat-Sheet
# Build & run
docker build -t myapp:latest .
docker run --rm -it -p 3000:3000 myapp:latest
# List & logs
docker ps
docker ps -a
docker logs -f <container>
# Images & cleanup
docker images
docker rmi <image>
docker system prune -a # remove unused objects
# Compose
docker-compose up --build -d
docker-compose down
# Save/pull/push
docker tag myapp:latest registry.example.com/myorg/myapp:1.0.0
docker push registry.example.com/myorg/myapp:1.0.0
docker pull registry.example.com/myorg/myapp:1.0.0
π From Local Docker to Kubernetes (high level)
- Push images to a registry.
- Create k8s manifests:
Deployment
,Service
,ConfigMap
,Secret
,PersistentVolumeClaim
. - Use
kubectl apply -f
to deploy. - Use
HorizontalPodAutoscaler
for scaling;Ingress
for routing. - Monitor with Prometheus + Grafana and manage lifecycle via Helm charts.
π― Final Notes & Checklist Before You Ship
- Image builds reproducible and small
- No secrets baked into images
- Non-root process in container
- Health checks + logs forwarded to aggregator
- Proper resource requests/limits (or runtime limits)
- CI builds image and pushes to registry
- Deployment uses immutable tags and supports rollback
✨ Closing — Why Docker Still Matters
Docker made containers mainstream by making images easy, builds fast, and deployments portable. Whether you’re developing locally, running CI pipelines, or operating at scale with Kubernetes — Docker is a foundational tool in modern software delivery.
Comments
Post a Comment