Containers
Lightweight, portable units that package code and all its dependencies so the application runs quickly and reliably across different computing environments.
Runs Anywhere
"Build once, run anywhere" — containers work identically on developer laptops, staging servers, and production clouds.
Lightweight
Containers share the host OS kernel, making them far more efficient than traditional VMs. Start in seconds, not minutes.
Isolated
Each container runs in its own isolated environment with its own filesystem, network, and process space — no conflicts.
Version Control
Docker images are versioned and immutable. Roll back to any previous version instantly. Reproducible builds every time.
Microservices
Perfect for microservices architecture — each service in its own container, independently deployable and scalable.
Dockerfile
Define imageBuild
Create imagePush
Ship to registryRun
Start containerRemove
Clean up| Feature | 🐳 Docker Containers | 🖥️ Virtual Machines |
|---|---|---|
| Startup Time | Seconds | Minutes |
| Size | MBs | GBs |
| Performance | Near-native | Overhead from hypervisor |
| OS Included | No (shared kernel) | Full OS per VM |
| Isolation | Process-level | Full hardware-level |
| Portability | Excellent | Limited |
| Resource Usage | Very low | High |
Docker Client (CLI)
The primary way users interact with Docker. Commands like docker run, docker build, and docker pull send instructions to the Docker daemon via REST API.
Docker Daemon (dockerd)
Listens for Docker API requests and manages Docker objects — images, containers, networks, and volumes. The daemon is the heart of Docker.
Docker Registry
Stores Docker images. Docker Hub is the default public registry. You can also run private registries (AWS ECR, GCR, self-hosted).
containerd
An industry-standard container runtime that manages the complete container lifecycle — from image transfer to execution and storage.
runc
The low-level OCI-compliant container runtime that actually creates and runs containers using Linux namespaces and cgroups.
REST API
An API interface that programs can use to talk to the daemon and instruct it. Both the CLI and Docker Compose communicate via this API.
Namespaces
Provide isolation for PID (processes), NET (networking), IPC (inter-process communication), MNT (filesystem mounts), and UTS (hostname).
Control Groups (cgroups)
Limit and account for resource usage (CPU, memory, disk I/O, network) for each container, preventing resource starvation.
Union File Systems
Overlay filesystems (OverlayFS) create efficient copy-on-write layered images, allowing containers to share base image layers.
A Docker image is a read-only template used to create containers. It contains everything needed to run an application — the code, runtime, libraries, environment variables, and config files.
Images are built in layers. Each instruction in a Dockerfile creates a new layer. Layers are cached and shared across images, making builds fast and storage efficient.
Key facts:
- Images are immutable — they never change once built
- Identified by name + tag (e.g.,
nginx:1.25) - Stored in registries (Docker Hub, ECR, GCR)
- Based on other images (parent/base images)
A container is a runnable instance of an image. You can create, start, stop, move, or delete a container. Multiple containers can run from the same image simultaneously.
Containers are ephemeral by default — any data written inside is lost when the container is removed (unless using volumes). Container state: created → running → paused → stopped → removed.
A Dockerfile is a text document with instructions to build a Docker image. Docker reads it top-to-bottom and each instruction creates a new layer.
Common instructions: FROM, RUN, COPY, ADD, ENV, EXPOSE, CMD, ENTRYPOINT, WORKDIR, VOLUME, ARG, LABEL, USER.
A registry is a stateless, server-side application that stores and distributes Docker images. Docker Hub is the default public registry.
Popular registries: Docker Hub, Amazon ECR, Google Container Registry (GCR), GitHub Container Registry (GHCR), Azure Container Registry (ACR), self-hosted with Harbor or Nexus.
Docker networks allow containers to communicate with each other and the outside world. Docker creates default networks automatically and you can create custom ones.
Containers on the same network can reach each other by container name (DNS resolution). Port mapping (-p host:container) exposes container ports to the host.
Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. They live outside the container's writable layer.
Volumes are managed by Docker and stored in /var/lib/docker/volumes/. They survive container restarts and removal, and can be shared among multiple containers.
| Command | Description |
|---|---|
| docker run [OPTIONS] IMAGE | Create and start a container from an image |
| docker run -d nginx | Run container in detached (background) mode |
| docker run -p 8080:80 nginx | Map host port 8080 to container port 80 |
| docker run --name myapp -v data:/app nginx | Run with a name and volume mounted |
| docker run -it ubuntu bash | Run interactively with terminal (TTY) |
| docker run -e NODE_ENV=prod app | Set environment variable in container |
| docker run --rm alpine echo "hi" | Remove container automatically after it stops |
| docker ps | List running containers |
| docker ps -a | List all containers (including stopped) |
| docker images | List all local images |
| docker pull nginx:latest | Pull an image from a registry |
| docker push myuser/myimage:tag | Push an image to a registry |
| docker build -t myapp:1.0 . | Build an image from Dockerfile in current dir |
| docker build --no-cache -t myapp . | Build without using layer cache |
| docker stop CONTAINER | Gracefully stop a running container (SIGTERM) |
| docker kill CONTAINER | Forcefully stop a container (SIGKILL) |
| docker start CONTAINER | Start a stopped container |
| docker restart CONTAINER | Stop and then start a container |
| docker rm CONTAINER | Remove a stopped container |
| docker rmi IMAGE | Remove an image |
| docker exec -it CONTAINER bash | Execute command inside running container |
| docker logs -f CONTAINER | Follow/stream container output logs |
| docker inspect CONTAINER | Display detailed info about container/image |
| docker stats | Live stream of container resource usage |
| docker top CONTAINER | Display running processes inside container |
| docker cp file.txt CONTAINER:/path/ | Copy files between host and container |
| docker commit CONTAINER myimage:v1 | Create image from container's current state |
| docker tag SOURCE_IMAGE TARGET_IMAGE | Create a new tag pointing to an image |
| docker login registry.example.com | Log in to a Docker registry |
| docker volume create mydata | Create a named volume |
| docker volume ls | List all volumes |
| docker volume rm mydata | Remove a volume |
| docker network create mynet | Create a custom network |
| docker network ls | List all networks |
| docker network connect mynet CONTAINER | Connect container to a network |
| docker system prune -af | Remove all unused containers, images, networks |
| docker info | Display system-wide Docker information |
| docker version | Show Docker client and server version info |
| docker search nginx | Search Docker Hub for images |
| docker history IMAGE | Show the build history/layers of an image |
| docker save -o image.tar myimage | Save image to a tar archive |
| docker load -i image.tar | Load image from a tar archive |
| docker pause / unpause CONTAINER | Pause or unpause processes in a container |
| docker port CONTAINER | List port mappings for a container |
| docker diff CONTAINER | Show filesystem changes in a container |
| docker events | Stream real-time events from Docker daemon |
# ── Stage 1: Builder ───────────────────────────────────── FROM node:20-alpine AS builder # Set working directory WORKDIR /app # Copy dependency files first (better layer caching) COPY package*.json ./ # Install dependencies RUN npm ci --only=production # Copy source code COPY . . # Build the application RUN npm run build # ── Stage 2: Production ────────────────────────────────── FROM node:20-alpine # Add metadata labels LABEL maintainer="dev@example.com" version="1.0" # Create non-root user for security RUN addgroup -S appgroup && adduser -S appuser -G appgroup WORKDIR /app # Copy only production build from builder stage COPY --from=builder --chown=appuser:appgroup /app/dist ./dist COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules # Set environment variables ENV NODE_ENV=production PORT=3000 # Build-time argument ARG BUILD_VERSION ENV BUILD_VERSION=$BUILD_VERSION # Declare volume for logs VOLUME ["/app/logs"] # Document the port EXPOSE 3000 # Switch to non-root user USER appuser # Health check HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ CMD wget -qO- http://localhost:3000/health || exit 1 # Default entrypoint ENTRYPOINT ["node"] # Default command (can be overridden) CMD ["dist/server.js"]
FROM
Sets the base image. Every Dockerfile starts with FROM. Use specific tags (not latest) for reproducibility. Alpine variants are smallest.
WORKDIR
Sets the working directory for subsequent RUN, CMD, ENTRYPOINT, COPY, ADD instructions. Creates the directory if it doesn't exist.
RUN
Executes commands in a new layer. Chain commands with && to minimize layers. Always clean up in the same RUN command.
COPY vs ADD
Prefer COPY for simple file copying. ADD has extra features (URL download, tar extraction) but COPY is more transparent and predictable.
ENV vs ARG
ENV sets runtime environment variables. ARG sets build-time variables only. Use ARG for build secrets, ENV for app configuration.
CMD vs ENTRYPOINT
ENTRYPOINT defines the executable. CMD provides default arguments. Together: ENTRYPOINT ["node"] + CMD ["app.js"] = node app.js.
# Dependencies node_modules/ # VCS .git/ .gitignore # Build artifacts dist/ build/ *.log # Secrets .env .env.* *.pem # Docker files themselves Dockerfile* docker-compose*
name: myapp services: frontend: build: context: ./frontend target: production ports: - "3000:3000" environment: - REACT_APP_API_URL=http://localhost:8080 depends_on: - api networks: - frontend-net api: build: ./backend ports: - "8080:8080" environment: - DATABASE_URL=postgres://user:pass@db:5432/mydb - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started networks: - frontend-net - backend-net restart: unless-stopped db: image: postgres:16-alpine volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql environment: POSTGRES_DB: mydb POSTGRES_USER: user POSTGRES_PASSWORD: pass healthcheck: test: ["CMD-SHELL", "pg_isready -U user"] interval: 10s timeout: 5s retries: 5 networks: - backend-net cache: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis_data:/data networks: - backend-net volumes: postgres_data: redis_data: networks: frontend-net: driver: bridge backend-net: driver: bridge internal: true # no external access
docker compose up # Start all services docker compose up -d # Start in detached mode docker compose down # Stop and remove containers docker compose down -v # Also remove volumes docker compose ps # List service containers docker compose logs -f # Follow all logs docker compose build # Build all service images docker compose pull # Pull latest images docker compose restart api # Restart specific service docker compose exec db psql # Exec into service docker compose scale api=3 # Scale a service to 3 replicas docker compose config # Validate and print config
Bridge
Default network driver. Containers can communicate via IP. Use custom bridges for DNS-based name resolution.
Host
Container shares the host's network stack directly. No port mapping needed. Best performance but no isolation.
None
Completely disables networking for the container. Useful for batch jobs and offline processing tasks.
Overlay
Spans multiple Docker hosts — used with Docker Swarm and Kubernetes for multi-host container communication.
Macvlan
Assigns a MAC address to containers, making them appear as physical devices on your network.
IPvlan
Similar to Macvlan but shares the host MAC address. Supports L2 and L3 modes for different routing needs.
# Create a custom bridge network docker network create --driver bridge myapp-net # Run containers on the same network docker run -d --name db --network myapp-net postgres docker run -d --name api --network myapp-net myapi # Containers can now reach each other by name: # api connects to db using hostname "db" # Port mapping: HOST_PORT:CONTAINER_PORT docker run -p 8080:80 nginx # TCP only docker run -p 53:53/udp dns-server # UDP port docker run -P nginx # Auto-assign all EXPOSED ports # Connect existing container to another network docker network connect mynet mycontainer # Inspect network details docker network inspect myapp-net
Named Volumes
Managed by Docker, stored in /var/lib/docker/volumes/. Best for production data persistence. Easy to back up.
Bind Mounts
Maps a host directory directly into the container. Great for development — changes on host reflect instantly in container.
tmpfs Mounts
Stored in host memory only, never written to disk. Useful for sensitive data (secrets, tokens) that shouldn't persist.
# ── Named Volumes ──────────────────────────────────────── docker volume create pgdata docker run -v pgdata:/var/lib/postgresql/data postgres # ── Bind Mounts ────────────────────────────────────────── # Mount current directory into container (dev mode) docker run -v $(pwd):/app -w /app node npm start # Read-only bind mount docker run -v /host/config:/app/config:ro myapp # ── tmpfs Mounts ───────────────────────────────────────── docker run --tmpfs /run:rw,noexec,nosuid,size=65536k myapp # ── Backup a Named Volume ──────────────────────────────── docker run --rm -v pgdata:/data -v $(pwd):/backup alpine \ tar czf /backup/pgdata-backup.tar.gz /data # ── Share volume between containers ────────────────────── docker run -d --name writer -v shared:/data writer-app docker run -d --name reader --volumes-from writer reader-app # ── Volume management ──────────────────────────────────── docker volume ls docker volume inspect pgdata docker volume prune # Remove unused volumes
Use Multi-Stage Builds
Separate build environment from runtime. Compile/install in a builder stage, copy only artifacts to the final slim image. Reduces image size by 60–90%.
Pin Specific Image Tags
Never use :latest in production. Pin to specific versions (e.g., node:20.11.1-alpine3.19) for reproducible, predictable builds.
Run as Non-Root User
Create and switch to a non-root user in your Dockerfile. Running as root is a security risk — if a container is compromised, the attacker gets root.
Optimize Layer Caching
Order Dockerfile instructions from least to most frequently changing. Copy dependency files before source code to maximize cache hits on rebuilds.
Use .dockerignore
Exclude node_modules, .git, build artifacts, and secrets from the build context. Speeds up builds and prevents leaking sensitive files.
One Process Per Container
Each container should have one responsibility. Avoid running multiple services (nginx + node + postgres) in a single container. Use Compose instead.
Add HEALTHCHECK Instructions
Define health checks so orchestrators know when a container is truly ready. Without this, traffic may be sent to containers that are starting up.
Never Hardcode Secrets
Don't put passwords, API keys, or tokens in Dockerfiles or images. Use Docker secrets, environment variables at runtime, or a secrets manager (Vault, AWS SSM).
Use Alpine/Distroless Base Images
Alpine images are ~5MB vs ~100MB for Debian-based. Smaller images = smaller attack surface, faster pulls, lower storage costs.
Scan Images for Vulnerabilities
Integrate docker scout, Snyk, or Trivy into CI/CD pipelines to catch vulnerabilities in base images and dependencies before they reach production.
Set Resource Limits
Always set --memory and --cpus limits in production to prevent a single container from starving the host or other containers of resources.
Use Named Volumes for Data
Prefer named volumes over bind mounts in production. They're portable, easier to manage with Docker APIs, and work correctly with backups and migrations.