← Back to Home ☸️ Kubernetes Guide
🐳  Complete Hands-On Reference

Docker Complete Guide

From container fundamentals to production-grade images, networking, volumes, registries, and Docker Compose — everything you need to ship confidently.

12
Chapters
2hr
Content
100%
Hands-On
01🐳

Introduction to Docker

Why Containers Changed Software Delivery

Docker is a tool designed to create, deploy, and run applications easily using containers. It packages your application along with all its dependencies into a single portable bundle called an image — so the app behaves identically everywhere it runs, from developer laptop to production server.
Problems Docker Solves
🔧
Works on My Machine — Fixed
Docker packages the application AND all dependencies into one image. No more version mismatches between dev, QA, staging, and production environments.
🔒
Application Isolation
Run multiple apps with conflicting dependencies on the same host inside isolated containers — no interference, no version conflicts between services.
Lightweight & Fast
Containers share the host OS kernel — no guest OS overhead. They start in seconds, not minutes. Much smaller in size than VMs (megabytes vs gigabytes).
🚀
Rapid Deployment
Build once, run anywhere. Same image runs on developer laptop, CI/CD pipeline, and production cluster. Deployment becomes predictable and repeatable.
Core Docker Concepts
📦
Image
A lightweight, standalone, executable package containing everything needed to run an application: code, runtime, libraries, environment variables, and config files. Think of it as a class blueprint.
🏃
Container
A running instance of an image. An isolated environment where your application executes. Multiple containers can run from the same image simultaneously — like objects from a class.
🗄️
Registry
A storage and distribution system for Docker images. Docker Hub is the public registry. AWS ECR, Azure ACR, and GCR are popular private registries.
🔧
Docker Engine
The client-server application that builds and runs containers. Includes the Docker daemon (server), REST API, and CLI client.
⚡ Advanced Engineering Notes

Docker uses Linux kernel features — namespaces (for isolation) and cgroups (for resource limits) — to create containers. Namespaces partition OS resources so each container sees its own isolated set (network, filesystem, process tree). cgroups limit how much CPU, memory, and I/O a container can use. On Mac and Windows, Docker Desktop runs a lightweight Linux VM to host the Docker daemon, which is why you'll find volume data stored inside that VM. In production, containers run natively on Linux hosts without any VM overhead.

02🔧

Docker Architecture

Engine, Daemon, Client & Registry

Docker uses a client-server architecture. The Docker client (CLI) sends commands to the Docker daemon, which does all the heavy lifting — building images, running containers, managing networks and volumes. The daemon pulls images from registries like Docker Hub when needed.
↳ Docker Architecture Overview
DOCKER CLIENTdocker builddocker pulldocker rundocker pushREST APIDOCKER HOSTDocker Daemon(dockerd) — builds & runsImagesContainersRunning Containersapidbuipull/pushREGISTRYDocker Hubhub.docker.comAWS ECRPrivate RegistryAzure ACRPrivate Registry
Architecture Components
ComponentRole
Docker Client (CLI)The tool you type commands into. Sends instructions to the daemon via REST API. Examples: docker build, docker run, docker push.
Docker Daemon (dockerd)Long-running background process. Manages images, containers, networks, and volumes. Listens for API requests.
Docker ImagesRead-only templates used to create containers. Stored as layered filesystems. Pulled from or pushed to registries.
Docker ContainersRunning instances of images. Isolated using Linux namespaces and cgroups. Has its own network interface, filesystem, and process space.
Docker RegistryStorage for Docker images. Docker Hub is the default public registry. AWS ECR, Azure ACR, GCR are private options.
Docker VolumesPersistent storage mechanism. Data survives container deletion. Managed by Docker CLI independently of containers.
⚡ Advanced Engineering Notes

The Docker daemon communicates with the container runtime (containerd) which actually creates and manages containers. This separation was introduced to allow Kubernetes and Docker to share containerd as a common runtime. On Linux, the Docker socket is at /var/run/docker.sock — mounting this socket into a container gives it full Docker control (dangerous in production). The Docker REST API is versioned — always check compatibility when upgrading Docker Engine in production environments.

03⚙️

Installing Docker

Docker Desktop & Docker Engine Setup

Docker Desktop is the recommended way to get Docker on Mac and Windows. It includes the Docker Engine, Docker CLI, Docker Compose, and a VM to host the Linux kernel. On Linux servers, you install Docker Engine directly — no VM required, maximum performance.
Prerequisites & Platform Options
PlatformInstall MethodNotes
Mac (Apple Silicon / Intel)Docker Desktop from docker.comIncludes Docker Engine, Compose, Kubernetes. Uses lightweight VM.
WindowsDocker Desktop with WSL2 backendWSL2 provides near-native Linux performance on Windows.
Ubuntu / DebianDocker Engine via apt repositoryapt-get install docker-ce docker-ce-cli containerd.io
RHEL / CentOSDocker Engine via yum repositoryyum install docker-ce — best for production servers.
All PlatformsVerify with: docker versionShows client + server version. Both must show correctly.
Post-Install Commands
docker versionVerify client and server are running correctly
docker infoFull system info — containers, images, storage driver
docker run hello-worldQuick sanity check — pulls and runs test image
systemctl enable dockerAuto-start Docker on server reboot (Linux)
usermod -aG docker $USERAdd current user to docker group (no sudo needed)
💡 Production Tip

On production Linux servers, always pin Docker to a specific version — avoid auto-upgrades that could break your workloads. Use: apt-mark hold docker-ce after installing. Test upgrades in staging first.

⚡ Advanced Engineering Notes

Docker Desktop on Mac runs a stripped-down Linux VM (using Apple's Hypervisor framework on M1/M2, or HyperKit on Intel). All container data, volumes, and the Docker daemon run inside this VM. This is why docker inspect shows volume paths like /var/lib/docker/volumes — that path is inside the VM, not on your Mac filesystem. On Apple Silicon (M1/M2/M3), Docker uses Rosetta 2 for amd64 images — most work fine but some performance-sensitive workloads may need arm64-native images. Use --platform linux/amd64 to force x86 emulation.

04💻

Essential Docker Commands

Images, Containers & Lifecycle Management

Docker CLI follows a consistent pattern: docker [management-command] [sub-command] [options]. Master these commands and you can manage any Docker environment confidently. Use --help with any command for detailed options.
Image Commands
docker imagesList all local images with size and tag
docker pull nginx:1.25Download specific image version from registry
docker pull nginx:latestDownload latest tag (default if no tag given)
docker rmi nginx:1.24Delete an image (must stop containers using it first)
docker image pruneRemove all dangling (untagged) images
docker image inspect nginxDetailed JSON info about an image
docker history nginxShow layers that make up the image
Container Commands
docker run nginxCreate and start a container (foreground)
docker run -d nginxRun container in detached (background) mode
docker run -d -p 8080:80 nginxMap port 8080 on host to port 80 in container
docker run --name my-nginx nginxAssign a custom name to the container
docker run -e MONGO_USER=admin mongoPass environment variable to container
docker psList running containers
docker ps -aList ALL containers (running + stopped)
docker stop my-nginxGracefully stop a container (sends SIGTERM)
docker start my-nginxStart a stopped container
docker restart my-nginxStop then start a container
docker rm my-nginxDelete a stopped container
docker rm -f my-nginxForce delete a running container
docker logs my-nginxView container logs
docker logs -f my-nginxStream container logs in real-time
docker exec -it my-nginx bashOpen interactive shell inside running container
docker rename old-name new-nameRename an existing container
docker inspect my-nginxFull JSON details about a container
docker statsReal-time CPU/memory usage of all containers
docker top my-nginxShow processes running inside container
Cleanup Commands
docker stop $(docker ps -q)Stop ALL running containers
docker rm $(docker ps -aq)Remove ALL stopped containers
docker system pruneRemove stopped containers, networks, dangling images
docker system prune -aRemove everything not used by running containers
💡 Port Mapping Rule

Host port must be unique — two containers cannot bind the same host port. But multiple containers can listen on the same container port (e.g., both on port 80 internally). Use -p 8080:80 -p 8081:80 for two nginx containers.

⚡ Advanced Engineering Notes

Docker commands evolved from docker run, docker ps etc. to docker container run, docker container ps in newer versions (management command format). Both work, but the management command format is cleaner in scripts. Use docker container ls --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" for readable output in CI pipelines. The --rm flag automatically removes the container when it exits — useful for one-off tasks and CI jobs to avoid container accumulation.

05🌐

Docker Networking

Bridge, Host & Custom Networks

Every container gets its own network namespace with a unique IP address. Docker provides several network drivers. Understanding networks is essential for connecting microservices, databases, and apps running in separate containers.
Network Types
🌉
Bridge (Default)
The default network for containers. Containers on the same bridge network can communicate by IP. The default bridge network does NOT support DNS-based container name resolution — use a custom bridge network for that.
🔀
Custom Bridge
User-defined bridge networks. Containers communicate using container NAMES (not just IPs) — Docker provides automatic DNS resolution. Always use custom bridge networks for multi-container apps.
🖥️
Host
Container shares the host's network namespace directly. No port mapping needed. Best performance. Not available on Docker Desktop (Mac/Windows). Use for performance-sensitive tools.
🔒
None
Container has no network access. Completely isolated. Use for batch jobs or security-sensitive processing that should never touch the network.
Network Commands
docker network lsList all Docker networks
docker network create my-netCreate a custom bridge network
docker network create --driver bridge my-netExplicitly specify bridge driver
docker network inspect my-netDetailed info including connected containers
docker network rm my-netDelete a network (no containers attached)
docker run --network my-net mongoRun container in a specific network
docker network connect my-net containerConnect running container to network
docker network disconnect my-net containerDisconnect container from network
Connecting MongoDB + Express (Hands-On)
BASH# Step 1: Create custom bridge network docker network create mongo-net # Step 2: Run MongoDB in custom network with a name docker run -d \ --name mongodb \ --network mongo-net \ -e MONGO_INITDB_ROOT_USERNAME=admin \ -e MONGO_INITDB_ROOT_PASSWORD=password \ mongo:4.4 # Step 3: Run Mongo Express — connect to MongoDB by container NAME docker run -d \ --name mongo-express \ --network mongo-net \ -p 8081:8081 \ -e ME_CONFIG_MONGODB_SERVER=mongodb \ mongo-express # Access at: http://localhost:8081 # "mongodb" resolves because both containers are on the same custom bridge network
💡 Key Difference

Default bridge: containers must use IP addresses to talk to each other. Custom bridge: containers talk by name (e.g., mongodb, api-service). Always use custom bridge networks for microservices.

⚡ Advanced Engineering Notes

Docker DNS works by embedding a DNS server inside the Docker daemon that resolves container names within the same user-defined network. This is why custom bridge networks support name resolution but the default bridge network does not — the default bridge predates the embedded DNS feature. In production, Docker overlay networks enable multi-host communication across a Docker Swarm cluster. For Kubernetes environments, the container networking is handled by CNI plugins (Calico, Cilium, Flannel) rather than Docker networking. MacVLAN and IPVLAN network drivers are used when containers need to appear as physical devices on the host network — useful in bare-metal networking scenarios.

06📄

Writing Dockerfiles

Instructions, Layers & Best Practices

A Dockerfile is a text document containing instructions to build a Docker image. Docker builds images by executing each instruction in order — each instruction creates a new read-only layer. Mastering Dockerfile instructions lets you build lean, secure, production-ready images.
Core Dockerfile Instructions
InstructionDescriptionExample
FROMSets the base image. Every Dockerfile starts with FROM.FROM openjdk:18-jdk-alpine
WORKDIRSets working directory for subsequent instructions.WORKDIR /app
COPYCopies files from build context to image filesystem.COPY target/app.jar /app/app.jar
ADDLike COPY but also supports URLs and tar extraction.ADD app.tar.gz /app/
RUNExecutes commands during build time — creates a new layer.RUN npm install
ENVSets environment variables available at runtime.ENV NODE_ENV=production
ARGBuild-time variable — passed via --build-arg flag.ARG JAVA_VERSION=18-jdk
EXPOSEDocuments which port the container listens on (informational).EXPOSE 8080
ENTRYPOINTDefines the command that always runs. Args are appended.ENTRYPOINT ["java", "-jar"]
CMDDefault command/args. Overridden by docker run arguments.CMD ["app.jar"]
VOLUMECreates a mount point for external volumes.VOLUME ["/data"]
LABELAdds metadata key-value pairs to the image.LABEL version="1.0"
Spring Boot Application Dockerfile
DOCKERFILE# ── Spring Boot Dockerfile ───────────────────────────────── # ARG allows passing Java version at build time: # docker build --build-arg JAVA_VERSION=18-jdk-alpine . ARG JAVA_VERSION=18-jdk-alpine FROM openjdk:${JAVA_VERSION} # Set working directory WORKDIR /app # Copy the pre-built JAR file COPY target/todo-api-1.0.0.jar todo-api.jar # Document the port (does not actually publish it) EXPOSE 8080 # Run the application ENTRYPOINT ["java", "-jar", "todo-api.jar"]
docker build -t todo-api:1.0.0 .Build image with tag from Dockerfile in current dir
docker build -f Dockerfile.prod -t app:prod .Build using a specific Dockerfile name
docker build --build-arg JAVA_VERSION=17-jdk -t app .Pass build-time argument
docker run -d -p 8080:8080 todo-api:1.0.0Run the built image
⚡ Advanced Engineering Notes

ENTRYPOINT vs CMD: ENTRYPOINT defines the fixed executable that always runs; CMD provides default arguments that can be overridden at docker run time. Combining them: ENTRYPOINT ["java", "-jar"] CMD ["app.jar"] — you can override just the jar name at runtime with docker run myimage different-app.jar. Use exec form (["java", "-jar"]) not shell form (java -jar) for ENTRYPOINT so the process receives OS signals directly — critical for graceful shutdown handling. ARG variables are NOT available at runtime (only during build). Use ENV for runtime variables. Never put secrets in ENV in Dockerfiles — use Docker secrets or environment injection at deployment time.

07

Docker Best Practices

Multi-Stage Builds, Caching & .dockerignore

Production-grade Docker images are lean, fast to build, and secure. Three key techniques transform a naive Dockerfile into a production-ready one: multi-stage builds (reduce image size dramatically), layer caching (speed up rebuild times), and .dockerignore (reduce build context size).
Multi-Stage Builds — React.js Example
DOCKERFILE# ── Stage 1: Build the React artifacts ───────────────────── FROM node:20-alpine AS builder WORKDIR /app # Copy only package files first (enables layer caching) COPY package*.json ./ RUN npm ci --only=production # Copy source code and build COPY . . RUN npm run build # ── Stage 2: Serve with Nginx ──────────────────────────────── # Only the artifacts from Stage 1 are copied — no node_modules! FROM nginx:stable-alpine # Copy built artifacts to nginx html directory COPY --from=builder /app/build /usr/share/nginx/html # Copy nginx config for React Router support COPY nginx.conf /etc/nginx/conf.d/default.conf CMD ["nginx", "-g", "daemon off;"] # Result: ~22MB image vs ~500MB single-stage image!
Layer Caching Strategy
DOCKERFILE# ── BAD: Source code changes invalidate npm install ───────── FROM node:20-alpine WORKDIR /app COPY . . # ← copies everything — cache busted on any change RUN npm install # ← re-runs every single time! # ── GOOD: Separate dependency install from source copy ─────── FROM node:20-alpine WORKDIR /app COPY package*.json ./ # ← only changes when dependencies change RUN npm install # ← cached unless package.json changes! COPY . . # ← source changes do NOT re-trigger npm install RUN npm run build
The .dockerignore File
BASH# .dockerignore — lives in same directory as Dockerfile # Docker sends entire build context (current dir) to daemon # .dockerignore prevents sending unnecessary files node_modules .git .gitignore *.md dist build .DS_Store *.log .env .env.* coverage __tests__ *.test.js Dockerfile*
Image Size Comparison
ApproachImage SizeBuild Time
Single-stage (npm start)~520 MBSlow (no caching)
Single-stage (serve build)~480 MBSlow (no caching)
Multi-stage (Nginx)~22 MBFast (with caching)
Multi-stage + cache + ignore~22 MBVery fast (cached layers)
Use alpine base images when possible — node:20-alpine is 180MB vs node:20 at 1GB
Copy package.json BEFORE source code to maximise layer cache hits
Use npm ci (clean install) not npm install in production Dockerfiles for reproducible builds
Add .dockerignore to exclude node_modules, .git, build artifacts from build context
Use multi-stage builds for compiled apps (React, Spring Boot, Go) to strip build tools
Pin base image versions explicitly — never use :latest in production Dockerfiles
Run as non-root user in production: adduser -D appuser && USER appuser
Scan images for vulnerabilities: docker scout quickview my-image or trivy image my-image
⚡ Advanced Engineering Notes

Layer caching works by hashing each instruction and its inputs. If the hash matches a cached layer, Docker reuses it without re-executing. COPY instructions are sensitive — Docker checksums file contents. For Go applications, use: COPY go.mod go.sum ./ then RUN go mod download before copying source. For Java Maven apps: COPY pom.xml ./ then RUN mvn dependency:go-offline. BuildKit (enabled by default in Docker 23+) adds parallel stage building, better caching, and cache mounts (RUN --mount=type=cache,target=/root/.m2 mvn package) that dramatically speed up builds in CI pipelines.

08💾

Docker Volumes

Data Persistence & Bind Mounts

Containers are ephemeral — when a container is deleted, all data written inside it is gone. Docker Volumes solve this by storing data outside the container lifecycle, on the host filesystem. Volumes are the recommended way to persist data for databases and stateful applications.
Storage Options Comparison
TypeDescriptionBest For
Named VolumeDocker manages the storage location. Created with docker volume create.Databases (MongoDB, PostgreSQL, MySQL)
Anonymous VolumeAuto-created by Docker with a random name. Not reusable easily.Temporary scratch space
Bind MountMaps a specific host directory path into the container.Dev workflow — edit code on host, see changes in container instantly
Named Volumes — MongoDB Example
BASH# Step 1: Create a named volume docker volume create mongo-data # Step 2: Run MongoDB with the volume mounted docker run -d \ --name mongodb \ --network mongo-net \ -e MONGO_INITDB_ROOT_USERNAME=admin \ -e MONGO_INITDB_ROOT_PASSWORD=password \ -v mongo-data:/data/db \ mongo:4.4 # /data/db is where MongoDB stores its data inside the container # It is now mapped to the "mongo-data" volume on the host # Step 3: Delete the container — data is PRESERVED docker stop mongodb && docker rm mongodb # Step 4: Recreate — data is RESTORED automatically docker run -d --name mongodb --network mongo-net \ -e MONGO_INITDB_ROOT_USERNAME=admin \ -e MONGO_INITDB_ROOT_PASSWORD=password \ -v mongo-data:/data/db mongo:4.4
Volume Commands
docker volume create mongo-dataCreate a named volume
docker volume lsList all volumes
docker volume ls -f name=mongoFilter volumes by name
docker volume inspect mongo-dataShow volume details including host path
docker volume rm mongo-dataDelete a volume (no container must be using it)
docker volume pruneRemove all unused volumes
docker inspect mongodbShows Mounts section with volume binding
Bind Mount — Development Workflow
BASH# Bind mount: map local source code into container # Changes on host reflect instantly in container docker run -d \ -p 3000:3000 \ -v /Users/dev/my-app:/app \ --name dev-server \ node:20-alpine sh -c "cd /app && npm install && npm start" # Common DB volume paths to mount: # MongoDB: -v my-vol:/data/db # PostgreSQL: -v my-vol:/var/lib/postgresql/data # MySQL: -v my-vol:/var/lib/mysql # Jenkins: -v my-vol:/var/jenkins_home
⚠️ Never delete volumes carelessly

Deleting a volume permanently destroys all data stored in it. Docker does NOT delete volumes when you delete containers — this is intentional protection. Always backup volume data before deleting. Use docker volume rm only when you are certain the data is no longer needed.

⚡ Advanced Engineering Notes

Volumes are stored at /var/lib/docker/volumes/ on Linux hosts and inside the Docker Desktop VM on Mac/Windows. Docker volume drivers (like Rex-Ray, Portworx, AWS EFS driver) enable volumes that span multiple hosts — essential for stateful containers in Docker Swarm or Kubernetes. In Kubernetes, this concept maps to PersistentVolumes and PersistentVolumeClaims. Bind mounts are great for development but should not be used in production — they tie the container to a specific host path and break portability. For application configuration in production, use Docker secrets or environment variables instead of bind-mounting config files.

09📦

Docker Registry & AWS ECR

Pushing & Pulling Images

A registry is storage and distribution for Docker images — like GitHub but for container images. Once you build an image locally, push it to a registry so it can be pulled on any other machine: CI/CD pipelines, staging servers, production clusters.
Registry Options
🌍
Docker Hub
Public/private registry. Free tier with limited private repos. Pull rate limits apply. hub.docker.com — the default registry Docker uses.
☁️
AWS ECR
Amazon Elastic Container Registry. Private, fully managed. Integrates with EKS, ECS, CodePipeline. Pay per storage and data transfer.
🔷
Azure ACR
Azure Container Registry. Integrates with AKS and Azure DevOps. Geo-replication available in premium tier.
🔑
Self-Hosted
Run docker run -d -p 5000:5000 registry:2 — your own private registry. Full control, no rate limits. Needs HTTPS for production.
Push to Docker Hub
BASH# Step 1: Login to Docker Hub docker login # Enter: username and password # Step 2: Tag your image with your Docker Hub username # Format: username/repository:tag docker tag todo-api:1.0.0 myusername/todo-api:1.0.0 # Step 3: Push to Docker Hub docker push myusername/todo-api:1.0.0 # Step 4: Pull on another machine docker pull myusername/todo-api:1.0.0
Push to AWS ECR
BASH# Step 1: Create ECR repository in AWS Console or CLI aws ecr create-repository --repository-name todo-api --region ap-south-1 # Step 2: Authenticate Docker to ECR aws ecr get-login-password --region ap-south-1 | \ docker login --username AWS --password-stdin \ 123456789.dkr.ecr.ap-south-1.amazonaws.com # Step 3: Tag image with ECR URI docker tag todo-api:1.0.0 \ 123456789.dkr.ecr.ap-south-1.amazonaws.com/todo-api:1.0.0 # Step 4: Push to ECR docker push 123456789.dkr.ecr.ap-south-1.amazonaws.com/todo-api:1.0.0 # Step 5: Pull on EC2 instance or EKS (automatically uses IAM role) docker pull 123456789.dkr.ecr.ap-south-1.amazonaws.com/todo-api:1.0.0
💡 ECR Lifecycle Policy

Set ECR lifecycle policies to auto-delete old image tags and control storage costs: keep only last 10 images per repository. In AWS Console → ECR → Repository → Lifecycle Policy.

⚡ Advanced Engineering Notes

ECR integrates seamlessly with IAM — EC2 instances and ECS tasks with the right IAM role can pull images without any explicit login command. For Kubernetes (EKS), use the ecr-credential-helper or the ECR public gallery for public images. ECR image scanning (powered by Clair/Inspector) automatically scans images for known CVEs on push. Enable immutable image tags in ECR production repos to prevent accidental tag overwriting — once pushed, a tag cannot be overwritten. Container image signing with AWS Signer or Cosign/Sigstore provides supply chain security verification.

10🔀

Docker Compose

Multi-Container Applications

Docker Compose lets you define and run multi-container applications with a single YAML file. Instead of running separate docker run commands for your API, database, and UI, Compose brings them all up together with one command: docker compose up.
Full Stack Docker Compose — React + Spring Boot + MongoDB
YAMLversion: "3.9" services: # ── MongoDB Database ────────────────────────────────── mongodb: image: mongo:4.4 container_name: mongodb environment: MONGO_INITDB_ROOT_USERNAME: admin MONGO_INITDB_ROOT_PASSWORD: password volumes: - mongo-data:/data/db # persist data networks: - app-network restart: unless-stopped # ── Spring Boot API ─────────────────────────────────── todo-api: image: myusername/todo-api:1.0.0 container_name: todo-api environment: SPRING_DATA_MONGODB_HOST: mongodb # uses container name as DNS SPRING_DATA_MONGODB_PORT: 27017 SPRING_DATA_MONGODB_DATABASE: todos ports: - "8080:8080" networks: - app-network depends_on: - mongodb # start after mongodb restart: unless-stopped # ── React.js UI ─────────────────────────────────────── todo-ui: image: myusername/todo-ui:1.0.0 container_name: todo-ui ports: - "3000:80" # nginx serves on port 80 networks: - app-network depends_on: - todo-api restart: unless-stopped # ── Named Volumes ───────────────────────────────────────── volumes: mongo-data: # ── Custom Network ──────────────────────────────────────── networks: app-network: driver: bridge
Docker Compose Commands
docker compose upStart all services (foreground — see logs)
docker compose up -dStart all services in detached mode
docker compose up -d todo-apiStart only a specific service
docker compose downStop and remove all containers + networks
docker compose stopStop services without removing containers
docker compose startStart stopped services
docker compose restart todo-apiRestart a single service
docker compose logsView logs from all services
docker compose logs -f todo-apiStream logs from a specific service
docker compose psShow status of all services
docker compose buildBuild/rebuild service images
docker compose pullPull latest images for all services
docker compose exec mongodb mongoshExecute a command in a running service
Key Compose Concepts
FeatureDescription
depends_onControls startup order. Service waits for listed services to be running (not healthy). Use with healthcheck for production.
networksServices on the same network communicate by service name as DNS. Auto-creates network if not specified.
volumesNamed volumes persist data across docker compose down/up cycles. Anonymous volumes do not.
restartnever / on-failure / always / unless-stopped. Use unless-stopped for production services.
environmentPass env vars to containers. Reference .env file variables with ${VAR_NAME}.
buildBuild image from local Dockerfile instead of pulling. Specify context and dockerfile path.
💡 Smart Partial Updates

When you change only one service in docker-compose.yml and run docker compose up -d — only that changed service is recreated. Other services are left untouched. This makes Compose very efficient for iterative development.

⚡ Advanced Engineering Notes

Docker Compose V2 (docker compose, no hyphen) is the current version — built in Go, ships with Docker Desktop, faster and more reliable than V1 (docker-compose, Python). Always use V2. Compose profiles (profiles: [dev]) let you define services that only start in certain environments — run docker compose --profile dev up to include dev-only services like mock servers or test databases. Compose Watch (docker compose watch) provides hot-reload for development: automatically rebuilds and restarts services when source files change — no more manual docker compose up --build cycles.

11⚖️

Docker vs Virtual Machines

Architecture, Performance & Trade-offs

Both containers and VMs solve the same fundamental problem: running multiple isolated workloads on shared hardware. But they solve it at different layers of the stack — VMs virtualise hardware, containers virtualise the OS. This architectural difference has massive implications for performance, size, and portability.
Architecture Comparison
AspectVirtual Machine (VM)Docker Container
Isolation LevelHardware virtualisation (hypervisor)OS-level virtualisation (namespaces/cgroups)
Guest OSFull OS per VM (GBs of overhead)Shares host OS kernel (no guest OS)
SizeGBs (OS + app + libs)MBs (app + libs only)
Startup TimeMinutes (boot full OS)Milliseconds (start process)
Resource UsageHigh (dedicated CPU/RAM per VM)Low (shared kernel, minimal overhead)
PortabilityImage tied to hypervisor typeRun on any Docker-compatible host
Security IsolationStronger (separate kernel per VM)Good but shared kernel is a larger attack surface
Use CaseLegacy apps, different OS, full isolationMicroservices, CI/CD, modern cloud-native apps
When to Use Each
🖥️
Use VMs When...
Running Windows and Linux workloads on the same host. Running applications that require full OS features. Compliance requirements demanding strong kernel isolation. Licensing requirements tied to specific OS versions.
🐳
Use Containers When...
Building microservices and cloud-native applications. Running many instances of the same service. CI/CD pipelines where fast build/run cycles matter. Modern 12-factor apps following containerisation principles.
🔀
Use Both Together
Most production environments combine both. VMs (EC2 instances) provide the infrastructure. Docker containers run on top of those VMs. Kubernetes orchestrates containers across the VM fleet.
⚡ Advanced Engineering Notes

AWS EC2 instances ARE virtual machines — when you run Docker containers on EC2, you have VMs running containers (both layers). AWS Fargate goes one step further: it abstracts away the EC2 VM and lets you run containers without managing any servers — fully serverless containers. The security model matters: containers share the host kernel, so a kernel exploit in one container potentially affects others. VMs with separate kernels prevent this. For highly sensitive multi-tenant workloads, use dedicated VMs per tenant or gVisor/Kata Containers which add a sandboxed kernel layer to containers, combining VM-level security with container-level performance.

12📋

Docker Quick Reference

Commands, Flags & Patterns

A quick-reference card for the most important Docker commands and patterns. Bookmark this chapter for fast lookups during development and production operations.
Most-Used Docker Commands
docker run -d -p 80:80 --name web nginxRun nginx detached on port 80
docker exec -it web bashInteractive shell into running container
docker logs web -f --tail=100Stream last 100 lines of logs
docker cp file.txt web:/app/Copy file from host to container
docker cp web:/app/log.txt .Copy file from container to host
docker commit web my-custom-imageCreate image from running container (avoid in prod)
docker save -o image.tar my-imageExport image to tar file
docker load -i image.tarImport image from tar file
docker stats --no-streamOne-time snapshot of resource usage
Dockerfile Quick Reference
FROMWORKDIRCOPYADDRUNENVARGEXPOSEENTRYPOINTCMDVOLUMELABELUSERHEALTHCHECKONBUILDSTOPSIGNAL
Key Flags Reference
FlagCommandMeaning
-ddocker runDetached mode (background)
-p host:containerdocker runPort mapping
-e KEY=valuedocker runEnvironment variable
-v vol:/pathdocker runVolume mount
--namedocker runAssign container name
--networkdocker runConnect to network
--rmdocker runAuto-remove on exit
-itdocker run / execInteractive terminal
-f / --filedocker buildSpecify Dockerfile name
-tdocker buildTag the built image
--build-argdocker buildPass ARG variable
-adocker psShow all containers (incl. stopped)
-qdocker psShow only container IDs
Production Best Practices Summary
Always use specific image tags in production — never :latest (non-deterministic builds)
Use multi-stage builds for compiled apps — reduces image size by 10-20×
Add .dockerignore to exclude node_modules, .git, .env from build context
Copy dependency files (package.json, pom.xml) before source code for better caching
Run as non-root user inside containers for security
Scan images with docker scout or trivy before pushing to production
Use named volumes for all database data — never rely on container filesystem
Set memory and CPU limits: docker run --memory=512m --cpus=1.0
Use health checks: HEALTHCHECK --interval=30s CMD curl -f http://localhost/health
Use restart: unless-stopped in docker-compose.yml for production services
Store secrets via environment injection or Docker secrets — never bake into images
Tag images with git commit SHA in CI/CD for full traceability
⚡ Advanced Engineering Notes

Docker BuildKit (now default in Docker 23+) unlocks advanced features: parallel multi-stage builds, cache mounts (persist package manager caches between builds), secret mounts (securely pass secrets during build without them landing in image layers), and SSH mounts (access private git repos during build). Cache mounts alone can reduce Node.js build times from 2 minutes to 10 seconds in CI. Example: RUN --mount=type=cache,target=/root/.npm npm ci. Container image signing with Cosign and the Sigstore project enables cryptographic verification that images haven't been tampered with between build and deployment — increasingly required in regulated industries and government cloud environments.