Master Docker containerization from the ground up. Learn Docker architecture, create containers, work with images, manage volumes and networks, and understand why containers are essential for cloud-native development.
Docker has revolutionized how we build, ship, and run applications. It solves the classic "it works on my machine" problem by packaging applications with all their dependencies into standardized containers that run consistently anywhere.
Containers provide lightweight, portable, and isolated environments that are much faster to start than virtual machines. They share the host OS kernel while maintaining process isolation, making them perfect for microservices architectures and cloud-native applications.
This lesson covers everything from Docker basics to advanced concepts like multi-stage builds, volumes, networks, and Docker Compose. You'll learn through practical examples and understand how Docker enables the modern development workflow.
Docker is a platform for developing, shipping, and running applications in containers. Understanding the problem it solves is key to appreciating its value.
The Problem Before Docker:
1Developer Machine:
2- Python 3.9
3- Node.js 16
4- PostgreSQL 14
5- Works perfectly!
6
7Production Server:
8- Python 3.7
9- Node.js 14
10- PostgreSQL 12
11- Everything breaks!Traditional Approach - Virtual Machines:
1┌────────────────────────────────┐
2│ Application │
3│ ├── Python 3.9 │
4│ ├── Dependencies │
5├────────────────────────────────┤
6│ Guest OS (Ubuntu) │
7│ (2GB+ RAM) │
8├────────────────────────────────┤
9│ Hypervisor │
10├────────────────────────────────┤
11│ Host OS │
12├────────────────────────────────┤
13│ Hardware │
14└────────────────────────────────┘
15
16Problems:
17- Slow startup (minutes)
18- Large size (GBs)
19- Resource heavyDocker Approach - Containers:
1┌────────────────────────────────┐
2│ Container 1 │ Container 2 │
3│ ├── App │ ├── App │
4│ ├── Libs │ ├── Libs │
5├────────────────┴───────────────┤
6│ Docker Engine │
7├────────────────────────────────┤
8│ Host OS │
9├────────────────────────────────┤
10│ Hardware │
11└────────────────────────────────┘
12
13Benefits:
14- Fast startup (seconds)
15- Small size (MBs)
16- Lightweight
17- Isolated processesKey Concepts:
Container: A running instance of an image. Lightweight, isolated, and contains everything needed to run the application.
Image: A read-only template with application code, libraries, and dependencies. Think of it as a snapshot or blueprint.
Dockerfile: A text file with instructions to build an image.
Registry: A repository for storing and distributing images (Docker Hub, ECR, etc.).
Real-World Example:
1# Developer builds and tests locally
2docker build -t myapp:v1 .
3docker run -p 3000:3000 myapp:v1
4# Works perfectly!
5
6# Push to registry
7docker push mycompany/myapp:v1
8
9# Deploy to production
10docker pull mycompany/myapp:v1
11docker run -p 3000:3000 mycompany/myapp:v1
12# Works identically!
13
14# The exact same container runs everywhere:
15# - Developer laptop
16# - CI/CD pipeline
17# - Staging server
18# - Production clusterDocker vs Virtual Machines:
1Comparison:
2┌─────────────────┬──────────┬──────────┐
3│ Feature │ Docker │ VM │
4├─────────────────┼──────────┼──────────┤
5│ Startup Time │ Seconds │ Minutes │
6│ Disk Space │ MBs │ GBs │
7│ Performance │ Native │ Overhead │
8│ Isolation │ Process │ Full OS │
9│ Portability │ High │ Medium │
10└─────────────────┴──────────┴──────────┘Docker uses a client-server architecture with several key components working together.
Docker Architecture:
1┌──────────────┐
2│ Docker CLI │ ← You run commands here
3└──────┬───────┘
4 │ REST API
5┌──────▼───────────────────────────┐
6│ Docker Daemon (dockerd) │
7│ ┌───────────────────────────┐ │
8│ │ Container Management │ │
9│ │ Image Management │ │
10│ │ Network Management │ │
11│ │ Volume Management │ │
12│ └───────────────────────────┘ │
13└──────────────┬───────────────────┘
14 │
15 ┌──────────┴──────────┐
16 │ │
17┌───▼────┐ ┌─────▼──────┐
18│ Images │ │ Containers │
19└────────┘ └────────────┘Docker Client (CLI):
1# The docker command you use
2docker run nginx
3docker build -t myapp .
4docker ps
5docker logs container-name
6
7# Talks to Docker daemon via REST API
8# Can connect to local or remote daemonDocker Daemon (dockerd):
1// The Docker daemon does the heavy lifting:
2// 1. Building images
3// 2. Running containers
4// 3. Managing networks
5// 4. Managing volumes
6// 5. Pulling/pushing images from registries
7
8// Runs as background service
9// Listens for Docker API requests
10// Manages Docker objects (images, containers, networks, volumes)Docker Images - Layered Filesystem:
1Image Layers (Read-Only):
2┌────────────────────────────┐
3│ Layer 4: COPY app.js │ ← Your app code
4├────────────────────────────┤
5│ Layer 3: RUN npm install │ ← Dependencies
6├────────────────────────────┤
7│ Layer 2: WORKDIR /app │ ← Working directory
8├────────────────────────────┤
9│ Layer 1: FROM node:18 │ ← Base image
10└────────────────────────────┘
11
12Container (Read-Write):
13┌────────────────────────────┐
14│ Thin writable layer │ ← Changes here
15├────────────────────────────┤
16│ Image layers (read-only) │
17└────────────────────────────┘Container Lifecycle:
1# Create container from image
2docker create nginx:latest
3
4# Start container
5docker start container-id
6
7# Or create and start in one command
8docker run nginx:latest
9
10# Container states:
11# Created → Running → Paused → Stopped → Removed
12
13# View running containers
14docker ps
15
16# View all containers (including stopped)
17docker ps -a
18
19# Stop container
20docker stop container-id
21
22# Remove container
23docker rm container-idDocker Registry:
1Docker Hub (public registry)
2├── Official Images
3│ ├── nginx
4│ ├── postgres
5│ └── redis
6└── User Images
7 └── username/myapp
8
9Private Registries:
10├── AWS ECR
11├── Google GCR
12├── Azure ACR
13└── Self-hosted registry
14
15# Pull image from registry
16docker pull nginx:latest
17
18# Tag image for registry
19docker tag myapp:latest username/myapp:v1
20
21# Push image to registry
22docker push username/myapp:v1Complete Workflow:
1# 1. Write Dockerfile
2cat > Dockerfile << 'EOF'
3FROM node:18-alpine
4WORKDIR /app
5COPY package*.json ./
6RUN npm install
7COPY . .
8EXPOSE 3000
9CMD ["node", "server.js"]
10EOF
11
12# 2. Build image
13docker build -t myapp:v1 .
14
15# 3. Run container
16docker run -d -p 3000:3000 --name myapp-container myapp:v1
17
18# 4. Interact with container
19docker logs myapp-container
20docker exec -it myapp-container sh
21
22# 5. Stop and remove
23docker stop myapp-container
24docker rm myapp-container
25
26# 6. Push to registry (optional)
27docker tag myapp:v1 username/myapp:v1
28docker push username/myapp:v1Learn to run, manage, and interact with Docker containers effectively.
Running Containers - docker run:
1# Basic run (foreground)
2docker run nginx
3# Ctrl+C to stop
4
5# Detached mode (background)
6docker run -d nginx
7# Returns container ID, keeps running
8
9# Named container
10docker run -d --name my-nginx nginx
11
12# Port mapping (host:container)
13docker run -d -p 8080:80 nginx
14# Access at http://localhost:8080
15
16# Environment variables
17docker run -d -e DATABASE_URL=postgres://... myapp
18
19# Multiple options combined
20docker run -d \
21 --name myapp \
22 -p 3000:3000 \
23 -e NODE_ENV=production \
24 -e DATABASE_URL=postgres://db:5432/mydb \
25 myapp:latestContainer Management Commands:
1# List running containers
2docker ps
3
4# List all containers (including stopped)
5docker ps -a
6
7# Detailed container info
8docker inspect container-name
9
10# Container resource usage
11docker stats container-name
12
13# View container logs
14docker logs container-name
15
16# Follow logs in real-time
17docker logs -f container-name
18
19# Show last 100 lines
20docker logs --tail 100 container-name
21
22# Stop container (graceful, sends SIGTERM)
23docker stop container-name
24
25# Kill container (immediate, sends SIGKILL)
26docker kill container-name
27
28# Restart container
29docker restart container-name
30
31# Pause/unpause container
32docker pause container-name
33docker unpause container-name
34
35# Remove container
36docker rm container-name
37
38# Force remove running container
39docker rm -f container-name
40
41# Remove all stopped containers
42docker container pruneExecuting Commands in Containers:
1# Run command in running container
2docker exec container-name ls -la
3
4# Interactive shell
5docker exec -it container-name sh
6docker exec -it container-name bash
7
8# Run as specific user
9docker exec -u root container-name whoami
10
11# Example: Debug running Node.js app
12docker exec -it myapp-container sh
13# Now inside container
14ps aux # See running processes
15cat /app/logs/app.log # Check logs
16npm list # Check installed packages
17exit # Leave containerReal-World Container Management:
1# Development workflow
2# Start database container
3docker run -d \
4 --name dev-postgres \
5 -e POSTGRES_PASSWORD=devpass \
6 -e POSTGRES_DB=myapp \
7 -p 5432:5432 \
8 -v pgdata:/var/lib/postgresql/data \
9 postgres:15-alpine
10
11# Start Redis cache
12docker run -d \
13 --name dev-redis \
14 -p 6379:6379 \
15 redis:7-alpine
16
17# Start your application
18docker run -d \
19 --name myapp \
20 -p 3000:3000 \
21 -e DATABASE_URL=postgres://postgres:[email protected]:5432/myapp \
22 -e REDIS_URL=redis://host.docker.internal:6379 \
23 myapp:latest
24
25# Check everything is running
26docker ps
27
28# View application logs
29docker logs -f myapp
30
31# Debug issues
32docker exec -it myapp sh
33
34# Cleanup when done
35docker stop myapp dev-postgres dev-redis
36docker rm myapp dev-postgres dev-redisContainer Resource Limits:
1# Limit memory
2docker run -d --memory="512m" nginx
3
4# Limit CPU
5docker run -d --cpus="1.5" nginx
6
7# Limit both
8docker run -d \
9 --name limited-app \
10 --memory="1g" \
11 --cpus="2.0" \
12 myapp:latest
13
14# View resource usage
15docker stats limited-appAuto-restart Policies:
1# Never restart (default)
2docker run -d --restart=no nginx
3
4# Always restart
5docker run -d --restart=always nginx
6
7# Restart on failure
8docker run -d --restart=on-failure nginx
9
10# Restart on failure, max 5 times
11docker run -d --restart=on-failure:5 nginx
12
13# Unless stopped manually
14docker run -d --restart=unless-stopped nginx
15# Survives system reboot unless you stopped itDockerfiles are blueprints for creating custom Docker images. Master the syntax and best practices.
Dockerfile Basics:
1# Simple Node.js application Dockerfile
2FROM node:18-alpine
3
4# Set working directory
5WORKDIR /app
6
7# Copy package files
8COPY package*.json ./
9
10# Install dependencies
11RUN npm install
12
13# Copy application code
14COPY . .
15
16# Expose port
17EXPOSE 3000
18
19# Command to run application
20CMD ["node", "server.js"]Dockerfile Instructions:
1# FROM - Base image
2FROM node:18-alpine
3FROM python:3.11-slim
4FROM nginx:alpine
5
6# WORKDIR - Set working directory
7WORKDIR /app
8WORKDIR /usr/src/app
9
10# COPY - Copy files from host to image
11COPY package.json /app/
12COPY . /app/
13COPY . /app/
14
15# ADD - Like COPY, but can extract tar and download URLs
16ADD https://example.com/file.tar.gz /app/
17# Prefer COPY for simple file copying
18
19# RUN - Execute command during build
20RUN npm install
21RUN apt-get update && apt-get install -y git
22RUN pip install -r requirements.txt
23
24# ENV - Set environment variables
25ENV NODE_ENV=production
26ENV PORT=3000
27ENV DATABASE_URL=postgres://...
28
29# EXPOSE - Document which ports to expose
30EXPOSE 3000
31EXPOSE 8080 8443
32
33# CMD - Default command when container starts
34CMD ["node", "server.js"]
35CMD ["python", "app.py"]
36CMD ["npm", "start"]
37
38# ENTRYPOINT - Configure container as executable
39ENTRYPOINT ["docker-entrypoint.sh"]
40ENTRYPOINT ["python", "app.py"]
41
42# VOLUME - Create mount point for volumes
43VOLUME ["/data"]
44VOLUME ["/app/uploads"]
45
46# USER - Set user for RUN, CMD, ENTRYPOINT
47USER node
48USER www-data
49
50# ARG - Build-time variables
51ARG VERSION=1.0.0
52ARG BUILD_DATE
53RUN echo "Building version $VERSION"
54
55# LABEL - Add metadata to image
56LABEL maintainer="[email protected]"
57LABEL version="1.0.0"
58LABEL description="My application"Multi-Stage Builds (Advanced):
1# Stage 1: Build stage
2FROM node:18 AS builder
3
4WORKDIR /app
5COPY package*.json ./
6RUN npm install
7
8COPY . .
9RUN npm run build
10# Build creates /app/dist directory
11
12# Stage 2: Production stage
13FROM node:18-alpine
14
15WORKDIR /app
16
17# Copy only production dependencies
18COPY package*.json ./
19RUN npm install --production
20
21# Copy built files from builder stage
22COPY /app/dist ./dist
23
24EXPOSE 3000
25CMD ["node", "dist/server.js"]
26
27# Result: Final image only has production code
28# Build tools and dev dependencies are left behind
29# Much smaller final image!Best Practices:
1# ✅ GOOD - Optimized Dockerfile
2
3# Use specific version tags
4FROM node:18.17.1-alpine3.18
5
6# Use ARGs for flexibility
7ARG NODE_ENV=production
8ENV NODE_ENV=$NODE_ENV
9
10WORKDIR /app
11
12# Copy dependency files first (better caching)
13COPY package*.json ./
14
15# Install dependencies
16RUN npm ci --only=production \
17 && npm cache clean --force
18
19# Copy application code last
20COPY . .
21
22# Don't run as root
23RUN addgroup -g 1001 -S nodejs \
24 && adduser -S nodejs -u 1001
25USER nodejs
26
27# Expose port
28EXPOSE 3000
29
30# Health check
31HEALTHCHECK \
32 CMD node healthcheck.js
33
34# Start application
35CMD ["node", "server.js"]Building Images:
1# Build with tag
2docker build -t myapp:v1 .
3
4# Build with multiple tags
5docker build -t myapp:v1 -t myapp:latest .
6
7# Build with build arguments
8docker build --build-arg VERSION=2.0.0 -t myapp:v2 .
9
10# Build without cache
11docker build --no-cache -t myapp:v1 .
12
13# Build specific Dockerfile
14docker build -f Dockerfile.prod -t myapp:prod .
15
16# View build history
17docker history myapp:v1
18
19# View image details
20docker inspect myapp:v1
21
22# Remove image
23docker rmi myapp:v1
24
25# Remove unused images
26docker image prune
27
28# Remove all images
29docker rmi $(docker images -q)Practical Example - Python Flask App:
1# Multi-stage build for Python Flask app
2FROM python:3.11-slim AS builder
3
4WORKDIR /app
5
6# Install build dependencies
7RUN apt-get update && apt-get install -y \
8 gcc \
9 && rm -rf /var/lib/apt/lists/*
10
11# Install Python dependencies
12COPY requirements.txt .
13RUN pip install --user --no-cache-dir -r requirements.txt
14
15# Production stage
16FROM python:3.11-slim
17
18WORKDIR /app
19
20# Copy installed packages from builder
21COPY /root/.local /root/.local
22
23# Copy application code
24COPY . .
25
26# Make sure scripts in .local are usable
27ENV PATH=/root/.local/bin:$PATH
28
29# Don't run as root
30RUN useradd -m -u 1000 appuser && chown -R appuser /app
31USER appuser
32
33EXPOSE 5000
34
35CMD ["python", "app.py"]Learn to manage persistent data with volumes, connect containers with networks, and orchestrate multi-container applications with Docker Compose.
Docker Volumes - Data Persistence:
1# Create named volume
2docker volume create postgres-data
3
4# Run container with volume
5docker run -d \
6 --name db \
7 -v postgres-data:/var/lib/postgresql/data \
8 postgres:15-alpine
9
10# List volumes
11docker volume ls
12
13# Inspect volume
14docker volume inspect postgres-data
15
16# Remove volume
17docker volume rm postgres-data
18
19# Remove unused volumes
20docker volume pruneVolume Types:
1# Named volume (managed by Docker)
2docker run -v mydata:/app/data nginx
3
4# Bind mount (specific host path)
5docker run -v /host/path:/container/path nginx
6docker run -v $(pwd):/app nginx # Current directory
7
8# Tmpfs mount (memory only, not persisted)
9docker run --tmpfs /tmp nginx
10
11# Volume with read-only flag
12docker run -v mydata:/app/data:ro nginxDocker Networks:
1# Create network
2docker network create myapp-network
3
4# Run containers on same network
5docker run -d --name db --network myapp-network postgres:15-alpine
6docker run -d --name app --network myapp-network myapp:latest
7
8# Inside 'app' container, can connect to 'db' by hostname:
9# postgres://db:5432/mydb
10
11# List networks
12docker network ls
13
14# Inspect network
15docker network inspect myapp-network
16
17# Remove network
18docker network rm myapp-networkNetwork Types:
1# Bridge (default) - containers can talk to each other
2docker network create --driver bridge my-bridge
3
4# Host - uses host network stack (no isolation)
5docker run --network host nginx
6
7# None - no networking
8docker run --network none nginx
9
10# Custom bridge with subnet
11docker network create \
12 --driver bridge \
13 --subnet 172.20.0.0/16 \
14 --gateway 172.20.0.1 \
15 custom-networkDocker Compose - Multi-Container Apps:
1# docker-compose.yml
2version: '3.8'
3
4services:
5 # PostgreSQL database
6 db:
7 image: postgres:15-alpine
8 environment:
9 POSTGRES_PASSWORD: secretpassword
10 POSTGRES_DB: myapp
11 volumes:
12 - postgres-data:/var/lib/postgresql/data
13 networks:
14 - backend
15 healthcheck:
16 test: ["CMD-SHELL", "pg_isready -U postgres"]
17 interval: 10s
18 timeout: 5s
19 retries: 5
20
21 # Redis cache
22 cache:
23 image: redis:7-alpine
24 networks:
25 - backend
26 healthcheck:
27 test: ["CMD", "redis-cli", "ping"]
28 interval: 10s
29
30 # Application
31 app:
32 build:
33 context: .
34 dockerfile: Dockerfile
35 ports:
36 - "3000:3000"
37 environment:
38 - NODE_ENV=production
39 - DATABASE_URL=postgres://postgres:secretpassword@db:5432/myapp
40 - REDIS_URL=redis://cache:6379
41 depends_on:
42 db:
43 condition: service_healthy
44 cache:
45 condition: service_healthy
46 networks:
47 - backend
48 - frontend
49 volumes:
50 - ./logs:/app/logs
51 restart: unless-stopped
52
53 # Nginx reverse proxy
54 nginx:
55 image: nginx:alpine
56 ports:
57 - "80:80"
58 volumes:
59 - ./nginx.conf:/etc/nginx/nginx.conf:ro
60 depends_on:
61 - app
62 networks:
63 - frontend
64 restart: unless-stopped
65
66volumes:
67 postgres-data:
68
69networks:
70 frontend:
71 backend:Docker Compose Commands:
1# Start all services
2docker-compose up
3
4# Start in detached mode
5docker-compose up -d
6
7# Build images and start
8docker-compose up --build
9
10# Stop all services
11docker-compose down
12
13# Stop and remove volumes
14docker-compose down -v
15
16# View running services
17docker-compose ps
18
19# View logs
20docker-compose logs
21
22# Follow logs for specific service
23docker-compose logs -f app
24
25# Execute command in service
26docker-compose exec app sh
27
28# Restart service
29docker-compose restart app
30
31# Scale service
32docker-compose up -d --scale app=3Complete Development Environment:
1# docker-compose.dev.yml
2version: '3.8'
3
4services:
5 postgres:
6 image: postgres:15-alpine
7 environment:
8 POSTGRES_PASSWORD: devpassword
9 POSTGRES_DB: myapp_dev
10 ports:
11 - "5432:5432"
12 volumes:
13 - postgres-dev:/var/lib/postgresql/data
14
15 redis:
16 image: redis:7-alpine
17 ports:
18 - "6379:6379"
19
20 app:
21 build:
22 context: .
23 dockerfile: Dockerfile.dev
24 ports:
25 - "3000:3000"
26 volumes:
27 - .:/app # Hot reload
28 - /app/node_modules # Don't overwrite node_modules
29 environment:
30 - NODE_ENV=development
31 - DATABASE_URL=postgres://postgres:devpassword@postgres:5432/myapp_dev
32 - REDIS_URL=redis://redis:6379
33 depends_on:
34 - postgres
35 - redis
36 command: npm run dev
37
38volumes:
39 postgres-dev:Usage:
1# Development
2docker-compose -f docker-compose.dev.yml up
3
4# Production
5docker-compose -f docker-compose.yml up -d
6
7# Development with rebuild
8docker-compose -f docker-compose.dev.yml up --buildYour progress is saved in this browser, on this device only.