🐳 Latest Edition
📖 Beginner to Advanced
⏱️ 50 min read
🎯 20+ Sections

⏱️ Estimated reading time: 45-50 minutes

📋 Quick Summary: Docker is the #1 containerization platform used by 90% of companies. It lets you package any application with its dependencies into a lightweight, portable container that runs everywhere — from your laptop to production servers to the cloud. Docker Swarm adds native clustering and orchestration. By the end of this course, you will containerize applications, build custom images, deploy with Docker Compose, and orchestrate clusters with Docker Swarm.

(Table of Contents)

🐳 What Is Docker?

Docker is a platform for developing, shipping, and running applications inside containers. A container is a lightweight, standalone, executable package that includes everything needed to run an application: code, runtime, system tools, libraries, and settings.

Think of containers as lightweight VMs — they isolate applications from each other and from the host system, but they share the host OS kernel, making them much faster and smaller than virtual machines.

Docker vs Virtual Machines

Feature Docker Container Virtual Machine
Startup Time Milliseconds Minutes
Size MBs GBs
Performance Near-native Some overhead
OS Kernel Shared with host Separate per VM
Isolation Process-level Hypervisor-level
Scalability 1000s per host 10s per host

🔧 Installation

Install Docker Engine

# Linux (Ubuntu/Debian) — recommended
curl -fsSL https://get.docker.com | sh

# Add your user to docker group (avoid sudo)
sudo usermod -aG docker $USER
# Log out and back in, or: newgrp docker

# macOS — install Docker Desktop
brew install --cask docker

# Windows — install Docker Desktop
winget install Docker.DockerDesktop

# Verify installation
docker --version              # Docker version 27.x
docker compose version         # Docker Compose v2
docker run hello-world         # Test container

Architecture Overview

# Docker uses a client-server architecture:
#   Client: docker CLI (commands you type)
#   Server: dockerd (Docker daemon — runs in background)
#   Registry: Docker Hub (stores images — like GitHub for containers)

# Key components:
#   Image:   Read-only template (e.g., python:3.12)
#   Container: Running instance of an image
#   Volume: Persistent storage outside container
#   Network: Communication between containers

📦 Docker Basics

First Commands

# Run a container
docker run nginx:latest
# Download the nginx image (if not cached) and run it

# Run in detached mode (-d)
docker run -d --name my-nginx -p 8080:80 nginx:latest
# -d: detached (background)
# --name: give it a name
# -p 8080:80: map host port 8080 to container port 80

# List containers
docker ps                    # Running containers
docker ps -a                 # All containers (including stopped)

# Stop and remove
docker stop my-nginx
docker rm my-nginx

# View logs
docker logs my-nginx
docker logs -f my-nginx      # Follow (tail -f style)

# Execute commands in running container
docker exec -it my-nginx bash

# Pull an image without running
docker pull python:3.12-slim

Lifecycle Commands

docker start container_name    # Start stopped container
docker stop container_name     # Graceful stop (SIGTERM)
docker kill container_name     # Force stop (SIGKILL)
docker restart container_name  # Stop + start
docker pause container_name    # Freeze processes
docker unpause container_name  # Unfreeze

docker rm container_name       # Remove container
docker rm $(docker ps -aq)     # Remove ALL containers
docker container prune         # Remove all stopped containers

🖼️ Docker Images

Images are the blueprints for containers. Understanding images is the most important Docker skill.

Image Commands

# List images
docker images

# Search images on Docker Hub
docker search nginx

# Pull an image
docker pull ubuntu:24.04
docker pull python:3.12-slim    # Slim = minimal footprint
docker pull alpine:latest       # Alpine = ~5MB (ultra small)

# Image information
docker inspect nginx:latest     # Full metadata (JSON)
docker history nginx:latest     # Layer history

# Remove images
docker rmi nginx:latest
docker image prune              # Remove dangling images
docker system prune             # Clean everything unused

Image Layers

# Docker images are built in LAYERS. Each instruction in a Dockerfile
# creates a new layer. Layers are cached and reused.

# Example: python:3.12-slim layers:
# 1. Base: debian:bookworm-slim (~80MB)
# 2. System: ca-certificates, apt packages (~30MB)
# 3. Python: Python 3.12.3 (~150MB)
# = Total: ~260MB (shared across all Python images!)

# Why layers matter:
# - Faster builds (cached layers don't rebuild)
# - Smaller downloads (only new/different layers)
# - Efficient storage (shared between images)

📝 Dockerfile — Build Your Own Images

The Dockerfile is a recipe for building images. Master this and you can containerize anything.

Python App Example

# Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install dependencies (separate from code — leverages cache)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port
EXPOSE 8000

# Default command
CMD ["python", "app.py"]
# Build and run
docker build -t my-python-app .
docker run -d -p 8000:8000 my-python-app

Node.js App Example

# Dockerfile (Multi-stage build — production optimized)
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

EXPOSE 3000
CMD ["node", "dist/server.js"]

# Build: docker build -t my-node-app .
# Result: Small production image (no build tools, no source code)

NGINX + Static Site

# Dockerfile
FROM nginx:alpine
COPY ./site /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

# Build and run
docker build -t my-site .
docker run -d -p 80:80 my-site

Dockerfile Instructions Reference

Instruction What It Does Example
FROM Base image to build from FROM python:3.12-slim
WORKDIR Set working directory WORKDIR /app
COPY Copy files from host COPY . .
RUN Execute commands during build RUN pip install -r req.txt
ENV Set environment variables ENV NODE_ENV=production
EXPOSE Document port (docs only) EXPOSE 8000
CMD Default command (can override) CMD ["python", "app.py"]
ENTRYPOINT Main command (harder to override) ENTRYPOINT ["nginx", "-g"]
ARG Build-time variables ARG VERSION=latest
HEALTHCHECK Container health check HEALTHCHECK curl localhost

Best Practices

# 1. Use .dockerignore (like .gitignore)
# .dockerignore:
**/__pycache__
**/.git
**/.env
**/node_modules

# 2. Order instructions by change frequency
# Stable first (apt, pip), code last
# This maximizes layer caching

# 3. Use specific tags, not 'latest'
FROM python:3.12-slim  # GOOD - specific
FROM python:latest     # BAD - breaks unpredictably

# 4. Combine RUN commands to reduce layers
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

# 5. Use multi-stage builds for smaller images
# See Node.js example above

# 6. Don't run as root
RUN useradd -m -u 1000 appuser
USER appuser

💡 Did You Know? A well-optimized Dockerfile can reduce image size by 90%+. Multi-stage builds alone can cut a 1.2GB Node.js image to under 150MB. The alpine variants are typically 5-10x smaller than full images.

🔗 Docker Compose

Compose lets you define and run multi-container applications with a single YAML file. This is how professional projects manage their services.

Basic Compose File

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    volumes:
      - .:/app          # Hot reload in dev
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypass
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

Compose Commands

# Start all services
docker compose up -d

# Start specific services
docker compose up -d web db

# Build and start (rebuilds images)
docker compose up -d --build

# View logs
docker compose logs -f
docker compose logs -f web

# Execute commands
docker compose exec web python manage.py migrate

# Check status
docker compose ps

# Stop all
docker compose down

# Stop and remove volumes (⚠️ destroys data!)
docker compose down -v

# Scale services
docker compose up -d --scale web=3

# View resource usage
docker compose top

Full-Stack Application

# docker-compose.yml (Production-ready Django app)
services:
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - static_volume:/static
    ports:
      - "80:80"
    depends_on:
      - web

  web:
    build: .
    command: gunicorn myproject.wsgi:application --bind :8000 --workers 4
    volumes:
      - static_volume:/app/staticfiles
    env_file: .env
    depends_on:
      - db
      - redis

  celery:
    build: .
    command: celery -A myproject worker -l info
    env_file: .env
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file: .env

  redis:
    image: redis:7-alpine

volumes:
  postgres_data:
  static_volume:

💾 Volumes & Persistent Data

Containers are ephemeral — when you remove them, all data inside is gone. Volumes solve this by storing data outside the container.

Volume Types

# 1. Named Volumes (managed by Docker — preferred)
docker volume create my_data
docker run -v my_data:/data image

# 2. Bind Mounts (direct host directory)
docker run -v /host/path:/container/path image
docker run -v $(pwd):/app image  # Common for dev

# 3. tmpfs (in-memory — fast, lost on stop)
docker run --tmpfs /app/cache image

# Volume commands
docker volume ls
docker volume inspect my_data
docker volume prune          # Remove unused volumes
docker volume rm my_data

Compose with Volumes

services:
  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # Init script

  web:
    image: myapp
    volumes:
      - .:/app              # Mount code for hot reload
      - /app/node_modules   # Don't override node_modules

volumes:
  postgres_data:

🌐 Networking

Network Types

# Docker networks:
# bridge:  Default. Containers communicate via IP. Good for standalone.
# host:    Container shares host's network (no isolation).
# none:    No network access.
# overlay: Multi-host networking (required for Swarm).

# Create networks
docker network create my-network
docker network create --driver overlay my-swarm-network  # For Swarm

# Connect containers
docker run --network my-network --name app1 nginx
docker run --network my-network --name app2 nginx
# Now app1 can ping 'app2' — DNS resolution is automatic!

# Network commands
docker network ls
docker network inspect my-network
docker network connect my-network container_name
docker network disconnect my-network container_name

DNS Resolution in Compose

# In Compose, every service name is DNS-resolvable:
#   web can reach: db:5432, redis:6379
# No need for IP addresses! Docker's embedded DNS handles it.

# To expose an internal service only (no ports):
services:
  db:
    image: postgres:16-alpine
    expose:
      - "5432"    # Accessible within Compose network, not externally

🔐 Environment Variables

# Method 1: Direct in command
docker run -e DATABASE_URL=postgresql://... myapp

# Method 2: .env file (reusable)
docker run --env-file .env myapp

# Method 3: In Compose
services:
  web:
    env_file: .env       # Load from file
    environment:          # Or inline
      - DEBUG=true
      - PORT=8000

# Docker secrets (Swarm only — more secure than env vars)
docker secret create db_password password.txt
docker service create --secret db_password myapp

⚙️ Docker Swarm Orchestration

Docker Swarm turns a group of Docker hosts into a single virtual cluster. It’s native to Docker — no additional tools needed.

Why Swarm?

  • Built into Docker — no extra installation
  • Simpler than Kubernetes — 10x less complexity
  • Good for small-medium clusters (3-50 nodes)
  • Load balancing built-in (routing mesh)
  • Desired state reconciliation — Swarm maintains the state you define
  • Rolling updates with zero downtime

Initialize a Swarm

# On the manager node:
docker swarm init --advertise-addr 10.0.0.1
# Output: docker swarm join --token SWMTKN-... 10.0.0.1:2377

# On worker nodes:
docker swarm join --token SWMTKN-... 10.0.0.1:2377

# View nodes
docker node ls

# Promote worker to manager
docker node promote node2

Swarm Services

In Swarm, you deploy services (not containers). A service defines the desired state:

# Create a service (3 replicas)
docker service create \
  --name web \
  --replicas 3 \
  --publish 80:80 \
  --network my-swarm-net \
  nginx:alpine

# List services
docker service ls

# List tasks (containers in the service)
docker service ps web

# Scale a service
docker service scale web=5

# Update image (rolling update)
docker service update \
  --image nginx:1.25 \
  --update-parallelism 1 \
  --update-delay 10s \
  web

# Inspect
docker service inspect --pretty web

# Remove service
docker service rm web

Swarm Mode Load Balancing

# Swarm's built-in routing mesh:
# 1. Deploy service with --publish 80:80
# 2. Swarm exposes port 80 on EVERY node
# 3. Request hits any node → forwarded to a healthy container
# 4. If a container fails, Swarm replaces it automatically

# Internal load balancing:
# Services resolve via DNS to virtual IP (VIP):
#   ping web → 10.0.0.2 (VIP)
#   The VIP load-balances across all 5 replicas

# Use --endpoint-mode dnsrr for DNS round-robin instead

Swarm Stacks (Compose for Swarm)

# docker-stack.yml (Compose file for Swarm)
version: '3.8'

services:
  web:
    image: myapp:latest
    ports:
      - "80:8000"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first   # Start new before stopping old
      restart_policy:
        condition: on-failure
        max_attempts: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.1'
          memory: 256M
    networks:
      - app-net

  db:
    image: postgres:16-alpine
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]  # DB on manager node
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-net

networks:
  app-net:
    driver: overlay

volumes:
  postgres_data:
    driver: local

# Deploy the stack
docker stack deploy -c docker-stack.yml myapp

# Stack commands
docker stack ls                    # List stacks
docker stack ps myapp             # List services in stack
docker stack services myapp       # List services
docker stack rm myapp             # Remove stack

Swarm Node Management

# Node states
docker node ls
# ID        HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
# abc123    node1     Ready   Active        Reachable
# def456    node2     Ready   Active        Leader

# Drain a node (move containers away for maintenance)
docker node update --availability drain node2
# All containers move to other nodes — zero downtime

# Activate it back
docker node update --availability active node2

# Add labels for placement constraints
docker node update --label-add region=us-east node1

# Then in stack file:
#   deploy:
#     placement:
#       constraints: [node.labels.region == us-east]

📊 Monitoring & Logging

# View container resource usage
docker stats                    # Live CPU/memory/network
docker stats --no-stream        # Single snapshot

# View logs
docker logs container_name          # All logs
docker logs -f container_name       # Follow mode
docker logs --tail 100 container_name  # Last 100 lines
docker logs --since 5m container_name  # Last 5 minutes

# Inspect container
docker inspect container_name       # Full JSON config

# Docker system commands (cleanup!)
docker system df                # Disk usage
docker system prune             # Remove unused (containers, networks, images)
docker system prune -a          # Remove ALL unused images
docker system prune --volumes   # Include volumes (⚠️ data loss!)

# Health checks in Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

🏗️ Real-World Project: Full-Stack App Deployment

Let’s deploy a complete Django + PostgreSQL + Redis + Celery application with Docker Compose and Swarm.

Step 1: Project Structure

myproject/
├── docker-compose.yml       # Dev environment
├── docker-stack.yml         # Production Swarm stack
├── Dockerfile               # App image
├── .dockerignore
├── .env
├── nginx/
│   └── default.conf         # Nginx config
├── backend/
│   ├── manage.py
│   ├── requirements.txt
│   └── myproject/
│       ├── settings.py
│       ├── urls.py
│       └── wsgi.py
└── static/                  # Static files

Step 2: Dockerfile

FROM python:3.12-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    gcc \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN python manage.py collectstatic --noinput

EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]

Step 3: Dev Compose

# docker-compose.yml
services:
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/app
    env_file: .env
    depends_on:
      - db
      - redis

  celery:
    build: .
    command: celery -A myproject worker -l info
    volumes:
      - .:/app
    env_file: .env
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myproject
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypass

  redis:
    image: redis:7-alpine

volumes:
  postgres_data:

Step 4: Production Swarm Stack

# docker-stack.yml
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    configs:
      - source: nginx_config
        target: /etc/nginx/conf.d/default.conf
    deploy:
      replicas: 2
      placement:
        constraints: [node.role == manager]
    depends_on:
      - web

  web:
    image: myregistry/myapp:${TAG:-latest}
    deploy:
      replicas: 4
      update_config:
        parallelism: 2
        delay: 10s
        order: start-first
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    environment:
      - DATABASE_URL=postgresql://db:5432/myproject
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  celery:
    image: myregistry/myapp:${TAG:-latest}
    command: celery -A myproject worker -l info
    deploy:
      replicas: 2
    environment:
      - DATABASE_URL=postgresql://db:5432/myproject
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]

volumes:
  postgres_data:
    driver: local

configs:
  nginx_config:
    file: ./nginx/default.conf

Step 5: Deploy Commands

# 1. Build and push image
docker build -t myregistry/myapp:v1.0 .
docker push myregistry/myapp:v1.0

# 2. Deploy stack
TAG=v1.0 docker stack deploy -c docker-stack.yml myapp

# 3. Verify
docker stack services myapp
docker service logs myapp_web -f

# 4. Update (zero-downtime)
docker build -t myregistry/myapp:v1.1 .
docker push myregistry/myapp:v1.1
TAG=v1.1 docker stack deploy -c docker-stack.yml myapp

# 5. Rollback if needed
docker service rollback myapp_web

❌ Common Mistakes & How to Avoid Them

🔴 Mistake #1: Running as Root

What happens: Containers run as root by default. If compromised, the attacker has root on your host.

How to fix: Use USER appuser in Dockerfile. Create user: RUN useradd -m appuser.

🔴 Mistake #2: Using :latest Tag

What happens: latest changes unpredictably. A rebuild might pull a different version and break everything.

How to fix: Always use specific tags: python:3.12-slim, nginx:1.25-alpine. Version your own images.

🔴 Mistake #3: Storing Secrets in Images

What happens: API keys and passwords baked into images. Anyone who pulls the image can extract them.

How to fix: Use environment variables at runtime. Never COPY or ENV secrets. Use Docker secrets in Swarm.

🔴 Mistake #4: Ignoring .dockerignore

What happens: COPY . . includes node_modules (500MB), .git (2GB), and **/__pycache__. Builds are slow, images are huge.

How to fix: Always maintain a .dockerignore with the same care as .gitignore.

🔴 Mistake #5: No Health Checks

What happens: Swarm thinks a broken container is “healthy” because the process is running but the app is stuck.

How to fix: Add HEALTHCHECK to Dockerfile or healthcheck to Compose/Stack.

🔴 Mistake #6: Bind Mounting in Production

What happens: Bind mounts tie containers to specific host paths. In Swarm, containers move between nodes — the mount breaks.

How to fix: Use named volumes or cluster-wide storage (NFS, EFS, GlusterFS) in production.

🔴 Mistake #7: One Container = One Process (Wrong)

What happens: People run both nginx AND the app in one container. Makes scaling impossible.

How to fix: One process per container. Use Compose to orchestrate multiple containers.

🧠 Test Your Knowledge

  1. What does docker-compose up -d --build do?

    A) Starts containers and shows logs

    B) Rebuilds images and starts containers in detached mode

    C) Downloads new images

    D) Removes old containers

    Answer: B — --build rebuilds images, -d starts in background.
  2. What’s the main difference between a bind mount and a named volume?

    A) Nothing, they’re the same

    B) Bind mounts use host paths; named volumes are managed by Docker

    C) Named volumes are faster

    D) Bind mounts persist after container removal

    Answer: B — Bind mounts point to host directories. Named volumes are Docker-managed and portable across hosts.
  3. In Docker Swarm, what does docker service scale web=5 do?

    A) Creates 5 different services

    B) Increases web service replicas to 5

    C) Restarts the service 5 times

    D) Distributes 5 ports

    Answer: B — Scale adjusts the number of running replicas for a service.
  4. Why should you COPY requirements.txt separately before the rest of the code?

    A) It’s required syntax

    B) To leverage layer caching — dependencies change less often than code

    C) It’s more secure

    D) It runs faster

    Answer: B — If requirements don’t change, Docker reuses the cached layer instead of reinstalling all packages.
  5. What’s a Docker stack?

    A) A single container

    B) A Compose file deployed to a Swarm cluster

    C) A group of Docker hosts

    D) A type of volume

    Answer: B — docker stack deploy takes a Compose file and deploys it as a Swarm stack.

❓ Frequently Asked Questions (FAQ)

Q1: Docker vs Kubernetes — which should I learn?

Learn Docker first. Kubernetes is built on Docker (or containerd). Docker Swarm is simpler (takes 1 hour to learn) and perfect for small-medium clusters. Kubernetes is more powerful but 10x more complex. For a solo developer or small team, Swarm + Docker Compose is often the right choice.

Q2: Can I run Docker on Windows?

Yes. Docker Desktop for Windows uses WSL2 (Windows Subsystem for Linux) to run Linux containers natively. You can also run Windows containers for .NET apps. Performance is nearly identical to native Linux.

Q3: How do I reduce Docker image size?

Use Alpine-based images (python:3.12-alpine vs python:3.12 — 150MB vs 900MB). Multi-stage builds. Combine RUN commands. Use .dockerignore. Remove package manager caches. Use distroless images for production (gcr.io/distroless/python3).

Q4: Can I use Docker in production?

Absolutely. Most of the internet runs on containers. Docker in production needs: orchestration (Swarm or K8s), monitoring (Prometheus/Grafana), logging (ELK stack), persistent storage (NFS/EFS), secrets management (HashiCorp Vault or Docker secrets), and CI/CD pipeline for automated builds and deployments.

Q5: What’s the difference between CMD and ENTRYPOINT?

CMD provides defaults that can be overridden: docker run myimage bash replaces CMD. ENTRYPOINT is the main command that always runs — arguments are appended. Pattern: use ENTRYPOINT for the executable and CMD for default arguments.

Q6: How do Docker and CI/CD work together?

Build the Docker image in CI (GitHub Actions, GitLab CI), run tests inside the container, push to registry, deploy to production. The same image that passes tests in CI goes to production — “build once, run anywhere.” This eliminates the “it works on my machine” problem.

Q7: What’s Docker’s security model?

Containers isolate processes using Linux namespaces and cgroups. They share the host kernel, so a kernel exploit can break isolation. For production: don’t run as root, use read-only root filesystem, limit capabilities, scan images for vulnerabilities (Trivy, Snyk), use secrets, and consider rootless Docker.

Q8: Can Docker run on a Raspberry Pi?

Yes. Docker runs on ARM architecture. curl -fsSL https://get.docker.com | sh works on Raspberry Pi OS. Use arm64v8 or arm32v7 images. Many official images now support ARM. A Pi 4 can run a small Swarm cluster for learning or low-traffic applications.

Q9: What’s the difference between docker-compose and docker stack?

docker-compose runs on a single host (development). docker stack deploys the same YAML to a Swarm cluster (production). The YAML format is mostly the same, but stack adds deploy: sections for replicas, update config, restart policy, and resource limits.

Q10: How do I back up Docker volumes?

Run a temporary container that mounts the volume and creates a backup: docker run --rm -v my_volume:/data -v $(pwd):/backup alpine tar czf /backup/volume-backup.tar.gz -C /data .. Restore: docker run --rm -v my_volume:/data -v $(pwd):/backup alpine tar xzf /backup/volume-backup.tar.gz -C /data.

📖 Glossary

Term Definition
Container A running instance of an image — isolated process with its own filesystem
Image A read-only template with instructions for creating a container
Dockerfile A script with instructions to build a Docker image
Volume Persistent storage that outlives containers
Bind Mount A host directory mounted into a container
Compose Tool for defining and running multi-container Docker applications with YAML
Swarm Docker’s native clustering and orchestration tool
Service In Swarm, a definition of how to run a container (image, replicas, ports, etc.)
Stack A group of Swarm services deployed together from a Compose file
Overlay Network Multi-host networking that spans all Swarm nodes
Routing Mesh Swarm’s built-in load balancer — any node can serve any service
Layer A filesystem change created by each Dockerfile instruction — layers are cached and reused

✅ Do’s & Don’ts

✅ Do ❌ Don’t
Use Alpine-based images for smaller builds Use :latest tag (unpredictable)
Use .dockerignore COPY everything blindly
Run as non-root user Run containers as root
Use multi-stage builds Ship build tools in production images
Use environment variables for config Hardcode secrets in Dockerfile
Add HEALTHCHECK Assume running = healthy

💡 10 Pro Tips Learned the Hard Way

  1. Learn docker inspect — it’s your best friend. docker inspect container_name returns everything: IP, mounts, environment variables, network settings, health status. Pipe it to jq for querying: docker inspect nginx | jq '.[0].NetworkSettings.IPAddress'.
  2. Use docker system prune weekly. Unused images, containers, volumes, and build cache accumulate fast. A 6-month-old Docker installation can easily waste 30GB of disk space.
  3. Pin base image versions with SHA256 hashes. Instead of FROM python:3.12-slim, use FROM python:3.12-slim@sha256:abc123def456.... This guarantees bit-identical builds forever. Update intentionally.
  4. Use labels for organization. Labels are metadata key-value pairs: LABEL maintainer="team@company.com" project="myapp" version="1.0". Search with docker ps --filter "label=project=myapp".
  5. Master docker-compose exec for debugging. Instead of SSH into servers, docker compose exec web bash gives you a shell inside the running container. docker compose exec web python manage.py shell for Django shell.
  6. Use HEALTHCHECK with curl. The simplest health check: HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1. Without it, Swarm has no idea your app is down.
  7. Swarm doesn’t need a load balancer. The routing mesh does Layer 4 load balancing automatically. For Layer 7 (path-based routing, SSL termination), add Traefik or Nginx as a proxy service.
  8. Always specify –restart policies. restart: unless-stopped in Compose, restart_policy: condition: on-failure in Swarm. Without these, containers that crash stay crashed.
  9. Use docker scan or Trivy for security. docker scan myimage checks for CVEs. trivy image myimage:latest is more comprehensive. Fix critical vulnerabilities before pushing to production.
  10. Containerize your development environment too. Use a devcontainer (VS Code Remote — Containers) so every dev on your team has the exact same environment. No more “but it works on my machine.”

🗺️ Learning Roadmap

Day Topic Goal ⏱️
1 Install & Basics Install Docker, run your first containers, basic commands 60 min
2 Dockerfiles Build images, best practices, multi-stage builds 90 min
3 Volumes & Networking Persistent data, networks, DNS resolution 90 min
4 Docker Compose Multi-container apps, environment variables, volumes in Compose 120 min
5 Docker Swarm Basics Init swarm, services, scaling, rolling updates 90 min
6 Swarm Stacks Deploy production stacks, secrets, configs, placement constraints 120 min
7 Production & CI/CD Monitoring, logging, security, CI/CD pipeline 120 min

🔍 Troubleshooting

⚠️ Problem 🔍 Cause ✅ Solution
Container exits immediately Process finishes or crashes Check docker logs. Use docker run -it for interactive processes.
Port already in use Another container or host process docker stop conflicting_container or change host port.
Permission denied User not in docker group sudo usermod -aG docker $USER then log out & back in.
No space left Unused images/volumes fill disk docker system prune -a --volumes (⚠️ removes all unused)
Can’t connect to service from another container Wrong network or DNS issue Both containers must be on the same network. Use service name, not localhost.

💬 What’s Your Experience?

What are you containerizing? Docker Swarm or Kubernetes? Drop a comment — I read every one.

Quick questions:

  • What was your biggest Docker challenge?
  • Swarm vs K8s — which do you use and why?
  • What’s the coolest thing you’ve containerized?
  • Which section helped you the most?

📌 TL;DR: If You Learn Nothing Else, Learn These 5

  1. Dockerfile Basics — FROM, WORKDIR, COPY, RUN, CMD. Multi-stage builds for small images.
  2. Docker Compose — Define all your services in one YAML. docker compose up -d and you’re running.
  3. Docker Swarmdocker swarm init creates a cluster. docker service create --replicas 3 deploys.
  4. Volumes & Networking — Named volumes for data persistence. Overlay networks for multi-host communication.
  5. Security — Don’t run as root. Pin image versions. Use secrets. Scan for CVEs.

More Free Courses on TricksPage

💭 Final Thoughts

Docker isn’t just a tool — it’s a fundamental shift in how we build and deploy software. Before Docker, every deployment was a unique snowflake. After Docker, every deployment is the same: “docker run myimage.”

The combination of Docker + Compose + Swarm covers 90% of deployment needs without the complexity of Kubernetes. For startups, small teams, and medium businesses, this stack is the sweet spot: powerful enough for production, simple enough for a single developer to manage.

🔥 Final Word: “It works on my machine” is the oldest excuse in software. Docker makes it work everywhere. Containerize everything. Your future self will thank you.

The best time to containerize was yesterday. The second best time is now. 🐳

More Free Courses on TricksPage

If this course helped you:

  • 📌 Bookmark this page for future reference
  • 📤 Share it with someone who needs it
  • 💬 Leave a comment — I read every one
  • Follow the blog for more deep courses

Leave a Reply

Your email address will not be published. Required fields are marked *