MASTERING
Docker
Containerization
Dockerfile · Docker Compose · Volumes · Networks
A Complete Beginner-to-Practitioner Study Guide
Images · Containers · Registries · Build Context · Multi-Stage Builds
Services · Scaling · Health Checks · Secrets · Production Patterns
container-1
container-2
container-3
Docker Containerization — Dockerfile & Docker Compose Page 2
TABLE OF CONTENTS
Contents
Placeholder for table of contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0
Docker Containerization — Dockerfile & Docker Compose Page 3
CHAPTER 1
Introduction to Containerization
What Docker solves, core concepts, and how containers differ from virtual machines.
1.1 The Problem Docker Solves
Before containers, the phrase "it works on my machine" was the most dreaded sentence in
software development. A developer's laptop might run Ubuntu 22.04 with Python 3.11 and a
specific set of library versions, while the production server runs RHEL 8 with Python 3.9. The
application would behave differently — or fail completely — across those environments.
Docker packages an application together with everything it needs to run: its runtime, system
libraries, configuration files, and dependencies — into a single, portable unit called a container.
The container runs identically on any machine that has the Docker engine installed, regardless of
the underlying operating system or installed software.
1.2 Containers vs Virtual Machines
Containers and virtual machines both provide isolation, but they do so at different levels of the
stack.
Dimension Virtual Machine Container
Isolates Full OS + kernel Processes only; shares host kernel
Startup time Minutes Sub-second to a few seconds
Disk footprint Gigabytes (full OS) Megabytes (only app layers)
Performance Near-native (hypervisor Native (no hypervisor)
overhead)
Density Dozens per host Hundreds per host
Portability Hypervisor-dependent Runs anywhere with Docker Engine
State Full OS state Stateless by default
CONCEPT Container isolation
A container is not a virtual machine. It is an isolated Linux process (or group of processes) that uses kernel
features called namespaces (for isolation of PIDs, network, filesystem, users) and cgroups (for CPU and
memory limits). The host kernel is shared, but each container sees its own private view of the system.
Docker Containerization — Dockerfile & Docker Compose Page 4
1.3 Core Docker Concepts
Image. A read-only, layered template from which containers are created. An image contains the
application code, runtime, libraries, and all dependencies. Images are built from a Dockerfile. Think
of an image as a class definition.
Container. A running instance of an image. A container is the image plus a writable layer on top.
You can run many containers from the same image simultaneously. Think of a container as an
object instantiated from the class.
Dockerfile. A plain-text script of instructions that tells Docker how to build an image. Each
instruction creates a new read-only layer in the image.
Registry. A storage and distribution system for Docker images. Docker Hub is the default public
registry. Private registries include AWS ECR, Google Artifact Registry, and GitHub Container
Registry.
Docker Engine. The daemon process (dockerd) running on the host that manages images,
containers, networks, and volumes. Clients (the docker CLI) talk to the engine via a REST API over
a Unix socket or TCP.
Layer. Each instruction in a Dockerfile adds a read-only filesystem layer on top of the previous
one. Layers are cached and shared between images, saving disk space and build time.
Volume. A Docker-managed directory that exists outside the container's writable layer, used to
persist data beyond the container's lifetime. Volumes survive container deletion.
Network. A virtual network that Docker creates to allow containers to communicate with each other
and with the outside world. Docker provides several network drivers: bridge, host, overlay, and
none.
1.4 Docker Architecture
Docker uses a client-server architecture. The Docker client (the docker CLI command you type in
a terminal) sends commands to the Docker daemon (dockerd), which does the actual work of
building, running, and distributing containers.
Docker architecture overview
Docker CLI (docker build / run / pull / push)
|
| REST API (Unix socket: /var/run/[Link])
v
Docker Daemon (dockerd)
■■■ containerd (container lifecycle)
■ ■■■ runc (OCI runtime: creates actual containers)
■■■ Image store (local /var/lib/docker)
■■■ Network subsystem
■■■ Volume subsystem
|
Docker Containerization — Dockerfile & Docker Compose Page 5
| pull / push
v
Registry (Docker Hub / ECR / GHCR / private)
1.5 Installing Docker
Shell — Install and verify Docker
# Linux (Debian/Ubuntu) — official script
curl -fsSL [Link] | sh
sudo usermod -aG docker $USER # avoid sudo for every command
newgrp docker
# Verify installation
docker --version # Docker version 26.x.x
docker info # engine details
docker run hello-world # first container!
NOTE rootless Docker
For security-sensitive environments, consider rootless Docker ([Link]), which runs
the Docker daemon and containers as a non-root user. Available since Docker 20.10.
Docker Containerization — Dockerfile & Docker Compose Page 6
CHAPTER 2
Docker CLI Essentials
The commands every Docker user needs every day — images, containers, and inspection.
2.1 Working with Images
Shell — Image management
# Pull an image from Docker Hub
docker pull nginx:1.25
docker pull python:3.12-slim
docker pull ubuntu:22.04
# List local images
docker images
docker image ls
docker image ls --format '{{.Repository}}:{{.Tag}} {{.Size}}'
# Remove an image
docker image rm nginx:1.25
docker rmi nginx:1.25 # short alias
docker image prune # remove dangling images
docker image prune -a # remove ALL unused images
2.2 Running Containers
Shell — docker run patterns
# Basic run — foreground, auto-removed on exit
docker run --rm hello-world
# Interactive terminal
docker run -it ubuntu:22.04 bash
# Detached (background) with a name
docker run -d --name webserver nginx:1.25
# Port mapping: host_port:container_port
docker run -d -p 8080:80 --name web nginx:1.25
# Environment variables
docker run -d \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
--name db postgres:16
# Volume mount: host_path:container_path
docker run -d \
-v /home/user/data:/var/lib/postgresql/data \
--name db postgres:16
Docker Containerization — Dockerfile & Docker Compose Page 7
# Named volume (Docker-managed)
docker run -d \
-v pgdata:/var/lib/postgresql/data \
--name db postgres:16
Flag Long form Purpose
-d --detach Run container in background
-it --interactive --tty Attach interactive terminal
-p H:C --publish H:C Map host port H to container port C
-e K=V --env K=V Set environment variable
-v --volume src:dst Bind-mount or named volume
src:dst
-w /path --workdir /path Set working directory inside container
--name N --name N Assign a name to the container
--rm --rm Auto-remove container on exit
--network --network N Connect to a specific network
N
--cpus N --cpus N Limit CPU (e.g. 0.5 = half a core)
-m 512m --memory 512m Limit memory
--restart --restart Restart policy
always|unless-stopped
2.3 Container Lifecycle
Shell — Container lifecycle
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop gracefully (SIGTERM, then SIGKILL after timeout)
docker stop webserver
# Kill immediately (SIGKILL)
docker kill webserver
# Start a stopped container
docker start webserver
# Restart
docker restart webserver
Docker Containerization — Dockerfile & Docker Compose Page 8
# Remove a stopped container
docker rm webserver
# Remove a running container (force)
docker rm -f webserver
# Remove all stopped containers
docker container prune
2.4 Inspecting and Debugging
Shell — Inspection and debugging
# Stream logs
docker logs webserver
docker logs -f webserver # follow (tail -f)
docker logs --tail 50 webserver # last 50 lines
# Execute a command inside a running container
docker exec -it webserver bash
docker exec webserver cat /etc/nginx/[Link]
# Inspect detailed metadata (JSON)
docker inspect webserver
docker inspect --format '{{.[Link]}}' webserver
# Resource usage (live)
docker stats
docker stats webserver
# Port mappings
docker port webserver
# Filesystem diff since start
docker diff webserver
# Copy files between host and container
docker cp webserver:/etc/nginx/[Link] ./[Link]
docker cp ./[Link] webserver:/usr/share/nginx/html/
Docker Containerization — Dockerfile & Docker Compose Page 9
CHAPTER 3
Dockerfile — Complete Reference
Every instruction explained in depth with theory, syntax, examples, and edge cases.
3.1 What Is a Dockerfile?
A Dockerfile is a plain-text file named exactly Dockerfile (no extension) containing a sequence of
instructions. Docker reads this file top to bottom and executes each instruction in order, creating a
new image layer for most instructions. The final stack of layers constitutes the image.
Each instruction that modifies the filesystem (FROM, RUN, COPY, ADD) creates a layer. Layers
are cached by Docker: if an instruction and all preceding instructions are unchanged since the last
build, Docker reuses the cached layer instead of re-executing it. This makes incremental builds
extremely fast.
Shell — docker build command
# Build an image from the Dockerfile in the current directory
docker build -t myapp:1.0 .
# Specify a different Dockerfile location
docker build -f docker/[Link] -t myapp:prod .
# Build with no cache
docker build --no-cache -t myapp:1.0 .
# Build with build-time arguments
docker build --build-arg NODE_VERSION=20 -t myapp:1.0 .
3.2 FROM — Base Image
Every Dockerfile must begin with a FROM instruction (except ARG instructions that precede it).
FROM specifies the base image from which the new image is built. The base image provides the
initial filesystem.
Dockerfile — FROM instruction
# Syntax
FROM <image>[:<tag>] [AS <name>]
# Examples
FROM ubuntu:22.04
FROM python:3.12-slim
FROM node:20-alpine
FROM scratch # empty filesystem
# Multi-stage: name a stage
FROM golang:1.22 AS builder
FROM [Link]/distroless/static AS runner
Docker Containerization — Dockerfile & Docker Compose Page 10
Tag choices matter. Tags like latest are mutable — the image they point to changes over time,
making builds non-reproducible. Always pin to a specific tag in production Dockerfiles. Use -alpine
variants for small images (Alpine Linux, ~5 MB) and -slim for Debian-based small images. Use
scratch as the base for statically compiled binaries (Go, Rust) when you want the absolute
minimum image.
3.3 RUN — Execute Commands
RUN executes a command during the build process and commits the result as a new layer. It is
used to install packages, compile code, create directories, and any other setup needed.
Dockerfile — RUN instruction
# Shell form (runs via /bin/sh -c on Linux)
RUN apt-get update && apt-get install -y curl
# Exec form (no shell, no variable expansion)
RUN ["apt-get", "install", "-y", "curl"]
# Best practice: combine into one RUN to minimise layers
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
git \
build-essential && \
rm -rf /var/lib/apt/lists/*
# Python dependencies
RUN pip install --no-cache-dir -r [Link]
# [Link] dependencies
RUN npm ci --only=production
BEST Minimise layers
Chain related commands with && in a single RUN instruction to keep layers small. Always clean up package
manager caches (rm -rf /var/lib/apt/lists/* for apt, pip --no-cache-dir, npm cache clean --force) in the same
RUN instruction as the install. Cleaning in a later RUN layer does NOT reduce image size — the data still
exists in the earlier layer.
3.4 COPY and ADD — Add Files
COPY and ADD both copy files from the build context (the directory you pass to docker build) into
the image filesystem.
Dockerfile — COPY and ADD
# COPY: preferred for simple file copying
COPY <src> <dst>
COPY src/ /app/src/
COPY [Link] /app/
COPY . /app # copy entire build context
Docker Containerization — Dockerfile & Docker Compose Page 11
# Copy multiple sources
COPY [Link] [Link] /app/
# Copy with ownership change (BuildKit)
COPY --chown=node:node . /app
# COPY from a named stage (multi-stage builds)
COPY --from=builder /build/output /app/
# ADD: additional features
ADD [Link] /app/ # download from URL
ADD [Link] /app/ # auto-extract tarballs
WARN Prefer COPY over ADD
Use COPY for local files — it is explicit about what it does. Use ADD only when you specifically need URL
downloading or automatic tarball extraction. ADD from URLs is not cached and should generally be
replaced with RUN curl or RUN wget so you have full control over the download.
3.5 WORKDIR — Set Working Directory
Dockerfile — WORKDIR
# Set the working directory for subsequent instructions
WORKDIR /app
# WORKDIR creates the directory if it doesn't exist
# All subsequent RUN, COPY, CMD, ENTRYPOINT use this as CWD
# Can be called multiple times
WORKDIR /app
COPY . .
WORKDIR /app/frontend
RUN npm install
NOTE Always set WORKDIR
Never use cd in RUN instructions. Use WORKDIR instead. Each RUN instruction starts a fresh shell, so cd
changes do not persist between RUN commands. WORKDIR persists for all subsequent instructions.
3.6 ENV — Environment Variables
Dockerfile — ENV
# Set environment variables available at build and runtime
ENV NODE_ENV=production
ENV APP_PORT=3000
# Multiple variables (preferred single-instruction style)
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
Docker Containerization — Dockerfile & Docker Compose Page 12
# Variables are available in subsequent RUN, CMD, ENTRYPOINT
RUN echo $NODE_ENV
# Override at runtime with docker run -e
docker run -e NODE_ENV=development myapp
3.7 ARG — Build-Time Arguments
ARG defines build-time variables that can be passed with docker build --build-arg NAME=value.
Unlike ENV, ARG values are NOT available in the final image at runtime — they only exist during
the build.
Dockerfile — ARG
ARG NODE_VERSION=20
ARG APP_VERSION
ARG BUILD_DATE
# Use the ARG value
FROM node:${NODE_VERSION}-alpine
# ARGs before FROM are special — they set the FROM value
ARG BASE_IMAGE=ubuntu:22.04
FROM ${BASE_IMAGE}
# After FROM, you need to re-declare to use the value
ARG APP_VERSION
RUN echo "Building version ${APP_VERSION}"
# Build with argument
docker build --build-arg NODE_VERSION=18 -t myapp .
WARN ARG security
Never use ARG to pass secrets (passwords, API keys). ARG values appear in docker history and can be
extracted from the image. Use Docker secrets or BuildKit secret mounts (RUN --mount=type=secret) for
sensitive values.
3.8 EXPOSE — Document Ports
Dockerfile — EXPOSE
# Document that the container listens on these ports
EXPOSE 80
EXPOSE 443
EXPOSE 5432
# TCP is the default; UDP can be specified
EXPOSE 5353/udp
# EXPOSE does NOT publish ports to the host!
# It is documentation for 'docker run -P' (publish all)
docker run -P myapp # maps all EXPOSEd ports to random host ports
Docker Containerization — Dockerfile & Docker Compose Page 13
3.9 CMD and ENTRYPOINT — Default Command
CMD and ENTRYPOINT define what runs when a container starts. Understanding their interaction
is one of the most important — and most confused — aspects of Docker.
Dockerfile — CMD and ENTRYPOINT
# CMD: default command, fully overridable at runtime
CMD ["nginx", "-g", "daemon off;"]
CMD ["python", "[Link]"]
CMD ["node", "[Link]"]
# Shell form (not recommended — PID 1 is /bin/sh, not your process)
CMD python [Link]
# ENTRYPOINT: fixed executable, args can be appended
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"] # default args to ENTRYPOINT
# At runtime:
docker run myimage # runs: nginx -g 'daemon off;'
docker run myimage -t # runs: nginx -t (override CMD)
docker run --entrypoint bash myimage # override ENTRYPOINT
# Common pattern: shell script as entrypoint
COPY [Link] /[Link]
RUN chmod +x /[Link]
ENTRYPOINT ["/[Link]"]
CMD ["nginx", "-g", "daemon off;"]
Combination ENTRYPOINT CMD Result
No ENTRYPOINT, CMD only — ["cmd"] runs cmd
ENTRYPOINT only ["ep"] — runs ep
Both (typical pattern) ["ep"] ["arg1"] runs ep arg1
docker run myimage X ["ep"] ["arg1"] runs ep X (CMD overridden)
Shell form CMD — cmd arg runs /bin/sh -c 'cmd arg'
3.10 VOLUME — Declare Mount Points
Dockerfile — VOLUME
# Declare a mount point for external volumes
VOLUME /data
VOLUME ["/var/log", "/var/db"]
# Any data written to /data is stored in a Docker-managed volume
# Data persists even when the container is deleted
# Mount a named volume at runtime
docker run -v myvolume:/data myimage
Docker Containerization — Dockerfile & Docker Compose Page 14
# Inspect the volume
docker volume inspect myvolume
3.11 USER — Non-Root Execution
Dockerfile — USER
# Create a non-root user and switch to it
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# Alpine Linux equivalent
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Set user by UID (avoids hardcoded usernames)
USER 1001:1001
# In multi-stage builds, switch back if needed
FROM base AS builder
USER root
RUN apt-get install ...
FROM base AS runner
USER appuser
BEST Always run as non-root
Running containers as root is a major security risk. If a container is compromised, a root container can
escape to the host with minimal barriers. Create a dedicated user with RUN useradd and switch with USER
before CMD/ENTRYPOINT.
3.12 HEALTHCHECK — Container Health
Dockerfile — HEALTHCHECK
# Define a health check command
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f [Link] || exit 1
# Exec form
HEALTHCHECK --interval=10s CMD ["pg_isready", "-U", "postgres"]
# Disable inherited health check from base image
HEALTHCHECK NONE
# Check status
docker inspect --format='{{.[Link]}}' mycontainer
docker ps # shows healthy/unhealthy in STATUS column
3.13 LABEL — Metadata
Dockerfile — LABEL
# Add metadata to the image
LABEL maintainer="team@[Link]"
Docker Containerization — Dockerfile & Docker Compose Page 15
LABEL version="1.0.0"
LABEL description="My application"
# OCI standard labels
LABEL [Link]="My App"
LABEL [Link]="1.0.0"
LABEL [Link]="2024-01-15T12:00:00Z"
LABEL [Link]="[Link]
# Multi-label (one instruction)
LABEL version="1.0" \
author="Alice" \
env="production"
# Query labels
docker inspect --format='{{json .[Link]}}' myimage
3.14 STOPSIGNAL and SHELL
Dockerfile — STOPSIGNAL and SHELL
# Change the signal sent to stop the container (default SIGTERM)
STOPSIGNAL SIGQUIT
STOPSIGNAL 9 # numeric
# Change the default shell used for shell-form RUN/CMD
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Enable pipefail so RUN fails if any pipe fails
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl [Link] | bash
Docker Containerization — Dockerfile & Docker Compose Page 16
CHAPTER 4
Dockerfile Best Practices &
Multi-Stage Builds
Lean images, layer caching, build security, and multi-stage patterns in depth.
4.1 Layer Caching Strategy
Docker's build cache is one of its most powerful features. When a layer is cached, Docker skips
re-executing that instruction and all the time it takes. The cache is invalidated for an instruction if:
(1) the instruction itself changes, (2) any preceding instruction has changed, or (3) for COPY/ADD,
the copied files have changed.
The critical insight is: place instructions that change rarely at the top of the Dockerfile, and
instructions that change frequently at the bottom. In a Python web application, the
[Link] rarely changes compared to the application source code.
Dockerfile — Layer caching: bad vs good
# BAD: cache broken on every source code change
FROM python:3.12-slim
WORKDIR /app
COPY . . # all source code — invalidates cache below
RUN pip install -r [Link] # re-runs every time!
CMD ["python", "[Link]"]
# GOOD: dependencies cached independently
FROM python:3.12-slim
WORKDIR /app
COPY [Link] . # copy only the requirements first
RUN pip install --no-cache-dir -r [Link] # cached!
COPY . . # copy source AFTER installing deps
CMD ["python", "[Link]"]
4.2 .dockerignore — Exclude Files
The .dockerignore file (in the same directory as the Dockerfile) tells Docker which files and
directories to exclude from the build context. A smaller build context means faster uploads to the
daemon and prevents accidentally including secrets or large unnecessary files.
.dockerignore file — what to exclude
# .dockerignore
.git
.gitignore
.env
*.env
secrets/
Docker Containerization — Dockerfile & Docker Compose Page 17
node_modules/
__pycache__/
*.pyc
*.pyo
dist/
build/
.pytest_cache/
.coverage
coverage/
[Link]
docs/
*.log
tests/
**/.DS_Store
4.3 Multi-Stage Builds
Multi-stage builds solve the problem of bloated production images. Without them, a compiled
application image must include all the build tools (compilers, make, npm, etc.) even though the final
container only needs the compiled binary or bundle.
A multi-stage Dockerfile uses multiple FROM instructions. Each FROM starts a new stage with a
clean filesystem. The final image only includes what you explicitly COPY from earlier stages — all
build tools, source code, and intermediate files are discarded.
Dockerfile — Multi-stage Go build
# Multi-stage build for a Go application
# ■■ Stage 1: Build ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM golang:1.22-alpine AS builder
WORKDIR /build
COPY [Link] [Link] ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags='-w -s' \
-o /build/app ./cmd/server
# ■■ Stage 2: Final image ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM [Link]/distroless/static:nonroot
COPY --from=builder /build/app /app
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app"]
# Result: golang:1.22 image ~280 MB -> final image ~5 MB
Dockerfile — Multi-stage [Link]/React build
# Multi-stage build for a [Link] React app
Docker Containerization — Dockerfile & Docker Compose Page 18
# ■■ Stage 1: Install dependencies and build ■■■■■■■■■■■■■■■■■■■■■■
FROM node:20-alpine AS builder
WORKDIR /app
COPY [Link] [Link] ./
RUN npm ci
COPY . .
RUN npm run build
# ■■ Stage 2: Serve with nginx ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM nginx:1.25-alpine AS runner
COPY --from=builder /app/dist /usr/share/nginx/html
COPY [Link] /etc/nginx/conf.d/[Link]
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Dockerfile — Multi-stage Python with test stage
# Multi-stage Python: separate test and production stages
# ■■ Stage 1: Base ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY [Link] .
RUN pip install --no-cache-dir -r [Link]
# ■■ Stage 2: Test ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM base AS test
COPY [Link] .
RUN pip install --no-cache-dir -r [Link]
COPY . .
RUN python -m pytest tests/ -v
# ■■ Stage 3: Production ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM base AS production
COPY --chown=appuser:appuser . .
RUN useradd -r appuser
USER appuser
CMD ["gunicorn", "app:app", "-w", "4", "-b", "[Link]:8000"]
# Build only production stage
docker build --target production -t myapp:prod .
# Build test stage
docker build --target test -t myapp:test .
4.4 BuildKit Secrets — Secure Credential Handling
Docker Containerization — Dockerfile & Docker Compose Page 19
Dockerfile — BuildKit secrets and SSH mounts
# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
# Mount a secret at build time (never stored in image layers!)
RUN --mount=type=secret,id=pip_token \
pip install --index-url [Link] /run/secrets/pip_token)@[Link]/simple my
# Build command
docker build \
--secret id=pip_token,src=./pip_token.txt \
-t myapp .
# SSH agent forwarding (for git clones over SSH)
RUN --mount=type=ssh git clone git@[Link]:org/[Link]
# Build with SSH
docker build --ssh default -t myapp .
4.5 Complete Production Python Dockerfile
Dockerfile — Complete production Python example
# syntax=docker/dockerfile:1
ARG PYTHON_VERSION=3.12
# ■■ Build stage ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM python:${PYTHON_VERSION}-slim AS builder
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
# Install build deps, create virtualenv
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libpq-dev && \
rm -rf /var/lib/apt/lists/* && \
python -m venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH
COPY [Link] .
RUN pip install -r [Link]
# ■■ Runtime stage ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
FROM python:${PYTHON_VERSION}-slim AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH=/opt/venv/bin:$PATH
Docker Containerization — Dockerfile & Docker Compose Page 20
# Runtime deps only (no gcc)
RUN apt-get update && \
apt-get install -y --no-install-recommends libpq5 curl && \
rm -rf /var/lib/apt/lists/* && \
useradd -r -u 1001 -g root appuser
WORKDIR /app
# Copy virtualenv and app from builder
COPY --from=builder /opt/venv /opt/venv
COPY --chown=appuser:root . .
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f [Link] || exit 1
LABEL [Link]="[Link] \
[Link]="My Python Application"
CMD ["gunicorn", "[Link]:application", \
"--bind", "[Link]:8000", \
"--workers", "4", \
"--timeout", "120"]
Docker Containerization — Dockerfile & Docker Compose Page 21
CHAPTER 5
Images, Registries, and Tagging
Naming, pushing, pulling, and managing images across registries.
5.1 Image Naming Conventions
A full Docker image name follows the pattern: registry/namespace/repository:tag
Image naming
# Full name
[Link]/myorg/myapp:v1.2.3
# Docker Hub (registry omitted by default)
myorg/myapp:v1.2.3
# Official image (namespace omitted)
nginx:1.25
python:3.12-slim
# Digest (immutable, SHA256 hash)
python@sha256:3d3b3f1c4c8a... # pinned, never changes
# Common tag conventions
myapp:latest # latest (avoid in production)
myapp:1.2.3 # semantic version
myapp:1.2 # minor version float
myapp:1 # major version float
myapp:git-abc1234 # git short SHA
myapp:20240115-abc123 # date + git SHA
myapp:prod # environment tag
5.2 Tagging and Pushing
Shell — Tagging and pushing images
# Tag an existing image
docker tag myapp:latest myorg/myapp:1.0.0
docker tag myapp:latest [Link]/myapp:1.0.0
# Login to a registry
docker login # Docker Hub
docker login [Link] # private registry
docker login -u AWS -p $(aws ecr get-login-password) [Link]
# Push image to registry
docker push myorg/myapp:1.0.0
docker push [Link]/myapp:1.0.0
# Push all tags
docker push myorg/myapp --all-tags
Docker Containerization — Dockerfile & Docker Compose Page 22
# Save image to a tar file (for transfer without a registry)
docker save myapp:1.0.0 | gzip > myapp_1.[Link]
# Load image from tar
docker load < myapp_1.[Link]
5.3 Image Inspection and Analysis
Shell — Image inspection
# Show image layers and history
docker history myapp:1.0.0
docker history --no-trunc myapp:1.0.0
# Detailed metadata
docker inspect myapp:1.0.0
docker inspect --format='{{.[Link]}}' myapp:1.0.0
# Analyse image size with dive (third-party)
dive myapp:1.0.0
# Show image disk usage
docker system df
docker system df -v # verbose, per-image
# Clean up
docker image prune # remove dangling images
docker image prune -a # remove all unused images
docker system prune # remove all unused resources
docker system prune -a # include unused images
Docker Containerization — Dockerfile & Docker Compose Page 23
CHAPTER 6
Volumes and Networking
Persist data with volumes, communicate between containers with networks.
6.1 Volume Types
Type Syntax When to Use
Named Volume -v myvolume:/data Persistent data (databases, uploads). Docker manages the
location.
Bind Mount -v /host/path:/data Development: share host source code into container.
tmpfs Mount --tmpfs /tmp Sensitive data in memory only, not written to disk.
Anonymous -v /data (no name) Temporary data. Hard to reference, prefer named volumes.
Volume
Shell — Volume management
# Create a named volume
docker volume create pgdata
# List volumes
docker volume ls
# Inspect a volume
docker volume inspect pgdata
# Use named volume in a container
docker run -d \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# Bind mount (development: live code reloading)
docker run -d \
-v $(pwd):/app \
-w /app \
node:20 \
npm run dev
# tmpfs for secrets in memory
docker run -d \
--tmpfs /tmp:rw,size=100m \
myapp
# Backup a volume
docker run --rm \
-v pgdata:/source:ro \
-v $(pwd):/backup \
Docker Containerization — Dockerfile & Docker Compose Page 24
busybox \
tar czf /backup/pgdata_backup.[Link] -C /source .
# Remove unused volumes
docker volume prune
6.2 Docker Networking
Driver Scope Use Case
bridge Single host Default. Containers on same host communicate via container name.
host Single host Container shares host network stack. Highest performance, least isolation.
overlay Multi-host Docker Swarm. Spans multiple Docker hosts.
macvlan Single host Container gets its own MAC address on the physical network.
none No network Complete network isolation.
Shell — Network management
# Create a custom bridge network
docker network create mynet
docker network create --driver bridge \
--subnet [Link]/16 mynet
# Connect containers — they can reach each other by name
docker run -d --name db --network mynet postgres:16
docker run -d --name app --network mynet myapp
# app can now connect to 'db' by hostname
# Connect a running container to a network
docker network connect mynet existing_container
# Disconnect
docker network disconnect mynet container
# List networks
docker network ls
# Inspect network (see connected containers, IPs)
docker network inspect mynet
# Remove network
docker network rm mynet
NOTE User-defined networks vs default bridge
Always create user-defined bridge networks instead of using the default docker0 bridge. On user-defined
networks, containers can reach each other by container name (automatic DNS). On the default bridge, only
IP addresses work by default.
Docker Containerization — Dockerfile & Docker Compose Page 25
CHAPTER 7
Docker Compose — Complete
Reference
Define and run multi-container applications with a single YAML file.
7.1 What Is Docker Compose?
Docker Compose is a tool for defining and running multi-container applications. Instead of running
separate docker run commands with many flags and manually wiring containers together, you
describe the entire application stack — services, networks, volumes — in a single YAML file
([Link] or [Link]). Then you start, stop, and rebuild everything with a single
command.
Compose V2 (integrated into the Docker CLI as 'docker compose') is the current version. Compose
V1 (the standalone docker-compose Python tool) is end-of-life. All examples in this chapter use V2
syntax.
Shell — Essential docker compose commands
# Core commands
docker compose up # create and start all services
docker compose up -d # detached (background)
docker compose up --build # rebuild images before starting
docker compose down # stop and remove containers + networks
docker compose down -v # also remove volumes
docker compose ps # list service containers
docker compose logs # view all logs
docker compose logs -f app # follow logs for 'app' service
docker compose exec app bash # shell into running service
docker compose run --rm app pytest # one-off command
docker compose build # build/rebuild images
docker compose pull # pull latest images
docker compose restart # restart all services
docker compose stop # stop without removing
7.2 Compose File Structure
[Link] — top-level structure
# [Link] — top-level keys
services: # Define application services
app:
...
db:
...
networks: # Define custom networks
frontend:
Docker Containerization — Dockerfile & Docker Compose Page 26
backend:
volumes: # Define named volumes
db_data:
redis_data:
secrets: # Define secrets (production)
db_password:
file: ./secrets/db_password.txt
configs: # Define configuration files
nginx_conf:
file: ./[Link]
# Compose file version is no longer required (Compose V2 ignores it)
7.3 services — All Keys Explained
[Link] — services: all keys
services:
app:
# Image to use (from registry or build)
image: myapp:1.0.0
# Build from Dockerfile instead of pulling
build:
context: ./app # build context directory
dockerfile: [Link]
args:
NODE_VERSION: "20"
APP_VERSION: "1.0.0"
target: production # multi-stage target
cache_from:
- type=registry,ref=myapp:cache
# Container name (instead of auto-generated)
container_name: myapp_prod
# Environment variables
environment:
NODE_ENV: production
DATABASE_URL: postgres://user:pass@db:5432/mydb
# Or from a file
env_file:
- .env
- .[Link]
# Port mapping
ports:
- "8080:3000" # host:container
- "[Link]:8080:3000" # bind to localhost only
- target: 3000
host_ip: [Link]
published: 8080
Docker Containerization — Dockerfile & Docker Compose Page 27
protocol: tcp
# Volumes
volumes:
- db_data:/var/lib/postgresql/data
- ./app:/app # bind mount
- /app/node_modules # anonymous (exclude)
# Networks
networks:
- frontend
- backend
# Service dependencies
depends_on:
db:
condition: service_healthy # wait for health check
redis:
condition: service_started # just wait for start
# Restart policy
restart: unless-stopped # no | always | on-failure | unless-stopped
# Resource limits
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "[Link]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Override command
command: ["node", "[Link]", "--port", "3000"]
# Override entrypoint
entrypoint: /[Link]
# Working directory
working_dir: /app
# User
user: "1001:1001"
# Labels
labels:
Docker Containerization — Dockerfile & Docker Compose Page 28
[Link]: "My App"
[Link]: "backend"
# stdin / tty
stdin_open: true # docker run -i
tty: true # docker run -t
# Expose ports to other services (not host)
expose:
- "3000"
# Extra hosts (/etc/hosts entries)
extra_hosts:
- "[Link]:host-gateway"
# Sysctls
sysctls:
[Link]: 1024
# Capabilities
cap_add:
- NET_ADMIN
cap_drop:
- ALL
# Read-only filesystem
read_only: true
tmpfs:
- /tmp
- /run
Docker Containerization — Dockerfile & Docker Compose Page 29
CHAPTER 8
Docker Compose — Practical
Patterns
Real-world Compose files for web apps, databases, reverse proxies, and more.
8.1 Full-Stack Web Application
[Link] — Full-stack web application
# [Link] — Python/FastAPI + PostgreSQL + Redis + Nginx
services:
# ■■ Nginx reverse proxy ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./[Link]:/etc/nginx/conf.d/[Link]:ro
- static_files:/var/www/static:ro
depends_on:
app:
condition: service_healthy
restart: unless-stopped
networks:
- frontend
# ■■ FastAPI application ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
app:
build:
context: .
target: production
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/appdb
REDIS_URL: redis://redis:6379/0
SECRET_KEY: ${SECRET_KEY}
volumes:
- static_files:/app/static
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "[Link]
interval: 30s
Docker Containerization — Dockerfile & Docker Compose Page 30
timeout: 5s
retries: 3
start_period: 10s
networks:
- frontend
- backend
# ■■ PostgreSQL ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
- ./[Link]:/docker-entrypoint-initdb.d/[Link]:ro
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- backend
# ■■ Redis cache ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- backend
networks:
frontend:
backend:
volumes:
pgdata:
redis_data:
static_files:
8.2 Development vs Production Overrides
Compose supports multiple files that are merged together. The pattern is to have a base
[Link] with shared configuration and environment-specific override files. Docker Compose
merges them using the -f flag or a COMPOSE_FILE environment variable.
[Link] — base file
# [Link] — base (shared between dev and prod)
Docker Containerization — Dockerfile & Docker Compose Page 31
services:
app:
build: .
environment:
APP_PORT: 8000
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
[Link] — development file
# [Link] — development overrides (auto-merged)
services:
app:
build:
target: development
environment:
DEBUG: "true"
RELOAD: "true"
volumes:
- .:/app # live code mount
ports:
- "8000:8000" # expose to host
db:
ports:
- "5432:5432" # expose DB to host for debugging
[Link] — production override file
# [Link] — production overrides
services:
app:
image: [Link]/myapp:${VERSION}
restart: always
deploy:
replicas: 3
resources:
limits:
memory: 512M
db:
restart: always
# Development (auto-merges [Link])
docker compose up -d
# Production (explicit merge)
docker compose -f [Link] -f [Link] up -d
8.3 Environment Variables and .env Files
[Link] — Environment variable patterns
Docker Containerization — Dockerfile & Docker Compose Page 32
# .env file — automatically loaded by Compose
DB_PASSWORD=supersecret
SECRET_KEY=my-jwt-secret-key
POSTGRES_USER=app
APP_VERSION=1.2.3
COMPOSE_PROJECT_NAME=myapp
# Reference in [Link] with ${VAR} or $VAR
services:
app:
image: myapp:${APP_VERSION}
environment:
SECRET_KEY: ${SECRET_KEY}
DB_URL: postgres://app:${DB_PASSWORD}@db:5432/mydb
# Variable interpolation with defaults
app:
environment:
LOG_LEVEL: ${LOG_LEVEL:-info} # default: info
PORT: ${PORT:?PORT must be set} # error if unset
8.4 Profiles — Optional Services
[Link] — Service profiles
services:
app:
image: myapp
# No profile = always started
db:
image: postgres:16
# No profile = always started
adminer:
image: adminer
profiles:
- debug # only started with --profile debug
ports:
- "8080:8080"
prometheus:
image: prom/prometheus
profiles:
- monitoring
grafana:
image: grafana/grafana
profiles:
- monitoring
# Start only core services
docker compose up -d
# Start with debug tools
Docker Containerization — Dockerfile & Docker Compose Page 33
docker compose --profile debug up -d
# Start with monitoring
docker compose --profile monitoring up -d
# Start with everything
docker compose --profile debug --profile monitoring up -d
8.5 Secrets in Compose
[Link] — Docker secrets
# [Link] with Docker secrets (Swarm mode or file-based)
services:
app:
image: myapp
secrets:
- db_password
- api_key
environment:
# App reads secrets from /run/secrets/
DB_PASSWORD_FILE: /run/secrets/db_password
db:
image: postgres:16
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt # file-based
api_key:
external: true # Swarm-managed secret
8.6 Scaling Services
[Link] — Scaling
# Scale a service to multiple instances
docker compose up -d --scale app=3
# In [Link] with deploy (used by Swarm and Compose)
services:
app:
image: myapp
deploy:
replicas: 3
update_config:
parallelism: 1 # update 1 replica at a time
delay: 10s # wait between updates
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
Docker Containerization — Dockerfile & Docker Compose Page 34
max_attempts: 3
# When scaling, do NOT use container_name (must be unique)
# Do NOT use fixed host ports (use a load balancer instead)
Docker Containerization — Dockerfile & Docker Compose Page 35
CHAPTER 9
Advanced Compose — Networks,
Volumes, and Startup Order
Fine-grained network control, volume drivers, and reliable startup sequencing.
9.1 Named Networks in Compose
[Link] — Network configuration
services:
nginx:
networks:
- frontend
app:
networks:
- frontend # reachable by nginx
- backend # can reach db and redis
db:
networks:
- backend # isolated: nginx cannot reach db directly
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # no external network access
# Use an externally created network
shared_net:
external: true
name: company_network
# Configure network options
custom:
driver: bridge
driver_opts:
[Link]: custom0
ipam:
config:
- subnet: [Link]/16
9.2 Volume Configuration
[Link] — Volume configuration
volumes:
Docker Containerization — Dockerfile & Docker Compose Page 36
# Simple named volume (Docker-managed)
pgdata:
# Volume with labels
redis_data:
labels:
[Link]: "daily"
# NFS volume (mount a network file share)
nfs_data:
driver: local
driver_opts:
type: nfs
o: addr=[Link],rw
device: :/mnt/sharedfolder
# External volume (created outside Compose)
existing_volume:
external: true
name: my_existing_volume
# tmpfs volume
cache_volume:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=100m
9.3 depends_on and Startup Order
By default, depends_on only waits for the dependent container to start — not for it to be ready. A
database takes several seconds to be ready after its process starts. The condition field solves this.
[Link] — depends_on with conditions
services:
app:
depends_on:
db:
condition: service_healthy # wait for health check to pass
restart: true # restart app if db restarts
redis:
condition: service_started # just wait for process start
migrations:
condition: service_completed_successfully # wait for job to finish
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
start_period: 20s
Docker Containerization — Dockerfile & Docker Compose Page 37
# One-shot migration job
migrations:
image: myapp
command: ["python", "[Link]", "migrate"]
depends_on:
db:
condition: service_healthy
restart: on-failure
WARN depends_on does not guarantee readiness by itself
service_started only means the container process has started, not that the application inside is ready.
Always define a healthcheck on services that others depend on, and use condition: service_healthy to wait
for the health check to pass. Alternatively, implement retry logic in your application code or use an init
container pattern with [Link].
9.4 Compose Networking Deep Dive
When you run docker compose up, Compose creates a default network named <project>_default.
Every service is automatically connected to this network and can reach other services by their
service name as the hostname.
[Link] — Networking details
# Services can reach each other by service name
# app can connect to postgres at host 'db' port 5432
services:
app:
environment:
DB_HOST: db # service name is the hostname
DB_PORT: 5432
db:
image: postgres:16
# dns_search and aliases
services:
app:
networks:
backend:
aliases:
- myapp
- api-server # reachable by these names too
# Connect to host network
services:
app:
network_mode: host # shares host network namespace
Docker Containerization — Dockerfile & Docker Compose Page 38
CHAPTER 10
Production Patterns and Security
Logging, security hardening, CI/CD integration, and operational best practices.
10.1 Logging
[Link] — Logging configuration
# View logs
docker compose logs app
docker compose logs -f --tail=100 app
# Logging driver configuration
services:
app:
logging:
driver: json-file # default
options:
max-size: "10m" # rotate at 10 MB
max-file: "3" # keep 3 rotated files
# Send to external log aggregator
app_prod:
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: myapp.{{.Name}}
# Splunk
app_splunk:
logging:
driver: splunk
options:
splunk-token: ${SPLUNK_TOKEN}
splunk-url: [Link]
10.2 Security Hardening
[Link] — Security hardening
services:
app:
image: myapp
user: "1001:1001" # non-root user
read_only: true # read-only root filesystem
tmpfs:
- /tmp # writable tmp in memory
- /run
security_opt:
- no-new-privileges:true # prevent privilege escalation
Docker Containerization — Dockerfile & Docker Compose Page 39
- seccomp:./[Link] # custom seccomp profile
cap_drop:
- ALL # drop all capabilities
cap_add:
- NET_BIND_SERVICE # only what's needed
environment:
- NODE_ENV=production
BEST Container security checklist
1. Run as non-root user. 2. Use read-only filesystem where possible. 3. Drop all capabilities; add only what
is needed. 4. Enable no-new-privileges. 5. Never bake secrets into images — use environment variables or
Docker secrets. 6. Scan images with docker scout, trivy, or grype. 7. Pin base image tags to specific digests
in production. 8. Use distroless or minimal base images.
10.3 Health Checks in Production
[Link] — Health check patterns
# Application-level health endpoint (FastAPI/Flask example)
# GET /health -> {"status": "ok"}
# Compose health check with condition
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "[Link]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
redis:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
10.4 CI/CD Integration
GitHub Actions — Docker build and push workflow
# GitHub Actions workflow
name: Build and Push Docker Image
Docker Containerization — Dockerfile & Docker Compose Page 40
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myorg/myapp:latest
myorg/myapp:${{ [Link] }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
APP_VERSION=${{ github.ref_name }}
10.5 Docker Scout — Image Vulnerability Scanning
Shell — Image scanning
# Scan an image for vulnerabilities
docker scout cves myapp:1.0.0
# Compare two images
docker scout compare myapp:1.0.0 myapp:0.9.0
# Quick overview
docker scout quickview myapp:1.0.0
# Trivy (open-source scanner)
trivy image myapp:1.0.0
trivy image --severity HIGH,CRITICAL myapp:1.0.0
# Grype
grype myapp:1.0.0
grype dir:.
Docker Containerization — Dockerfile & Docker Compose Page 41
CHAPTER 11
Complete Real-World Examples
End-to-end Dockerfile and Compose configurations for common application stacks.
11.1 [Link] API + MongoDB + Redis
Dockerfile — [Link] API
# Dockerfile — [Link] API
# syntax=docker/dockerfile:1
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine AS base
WORKDIR /app
# Install production dependencies
FROM base AS deps
COPY [Link] [Link] ./
RUN npm ci --only=production
# Development
FROM base AS dev
COPY [Link] [Link] ./
RUN npm ci
COPY . .
CMD ["node", "--inspect=[Link]", "src/[Link]"]
# Production
FROM base AS prod
RUN addgroup -S nodejs && adduser -S nodeuser -G nodejs
COPY --from=deps /app/node_modules ./node_modules
COPY --chown=nodeuser:nodejs . .
USER nodeuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s \
CMD node [Link] || exit 1
CMD ["node", "src/[Link]"]
[Link] — [Link] + MongoDB + Redis
# [Link] — [Link] + MongoDB + Redis
services:
api:
build:
context: .
target: prod
ports:
- "3000:3000"
environment:
Docker Containerization — Dockerfile & Docker Compose Page 42
NODE_ENV: production
MONGO_URL: mongodb://mongo:27017/mydb
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET}
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- app_net
mongo:
image: mongo:7
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
volumes:
- mongo_data:/data/db
- ./[Link]:/docker-entrypoint-initdb.d/[Link]:ro
healthcheck:
test: ["CMD", "mongosh", "--eval", "[Link]('ping')"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
networks:
- app_net
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- app_net
networks:
app_net:
volumes:
mongo_data:
redis_data:
11.2 Django + PostgreSQL + Celery + RabbitMQ
[Link] — Django + PostgreSQL + Celery + RabbitMQ + Nginx
# [Link] — Django with Celery workers
services:
django:
Docker Containerization — Dockerfile & Docker Compose Page 43
build: .
command: gunicorn [Link]:application --bind [Link]:8000 -w 4
environment:
DJANGO_SETTINGS_MODULE: [Link]
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/appdb
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
SECRET_KEY: ${DJANGO_SECRET_KEY}
volumes:
- static_files:/app/staticfiles
- media_files:/app/media
depends_on:
db:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
networks:
- web
- internal
celery_worker:
build: .
command: celery -A config worker -l INFO --concurrency=4
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/appdb
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
depends_on:
db:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
networks:
- internal
celery_beat:
build: .
command: celery -A config beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/appdb
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
depends_on:
db:
condition: service_healthy
restart: unless-stopped
networks:
- internal
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
Docker Containerization — Dockerfile & Docker Compose Page 44
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- internal
rabbitmq:
image: rabbitmq:3.12-management-alpine
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
networks:
- internal
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./[Link]:/etc/nginx/conf.d/[Link]:ro
- static_files:/var/www/static:ro
- media_files:/var/www/media:ro
depends_on:
- django
restart: unless-stopped
networks:
- web
networks:
web:
internal:
internal: true
volumes:
pgdata:
rabbitmq_data:
static_files:
media_files:
Docker Containerization — Dockerfile & Docker Compose Page 45
CHAPTER 12
Quick Reference
All Dockerfile instructions, Compose keys, and essential CLI commands.
12.1 Dockerfile Instructions
Instruction Syntax Purpose
FROM FROM image[:tag] [AS name] Set base image; start new build stage
RUN RUN cmd or RUN ["cmd","arg"] Execute command and commit layer
COPY COPY src dst Copy files from build context
ADD ADD src dst COPY + URL download + tarball extract
WORKDIR WORKDIR /path Set working directory
ENV ENV KEY=VALUE Set environment variable (build+runtime)
ARG ARG name[=default] Build-time variable only
EXPOSE EXPOSE port[/protocol] Document listening port
CMD CMD ["executable","arg"] Default command (overridable)
ENTRYPOINT ENTRYPOINT ["executable"] Fixed executable
VOLUME VOLUME /path Declare mount point
USER USER user[:group] Switch to user
HEALTHCHECK HEALTHCHECK [OPTIONS] CMD Define health test
cmd
LABEL LABEL key=value Add metadata
ONBUILD ONBUILD INSTRUCTION Trigger in child image build
STOPSIGNAL STOPSIGNAL signal Signal to stop container
SHELL SHELL ["shell","flag"] Override default shell
12.2 Docker CLI — Images
Command Description
docker build -t name:tag . Build image from Dockerfile in current dir
Docker Containerization — Dockerfile & Docker Compose Page 46
Command Description
docker build -f FILE -t name . Build using specific Dockerfile
docker build --no-cache -t n . Build without using cache
docker pull image:tag Pull image from registry
docker push image:tag Push image to registry
docker images List local images
docker image rm image:tag Remove image
docker image prune Remove dangling images
docker image prune -a Remove all unused images
docker history image Show layer history
docker inspect image Detailed image metadata
docker tag src dst Tag an image with a new name
docker save image | gzip > f Export image to tar
docker load < [Link] Import image from tar
12.3 Docker CLI — Containers
Command Description
docker run -d -p H:C --name n image Run container detached with port mapping
docker run -it --rm image bash Interactive shell, auto-remove
docker run -v vol:/path image Run with volume
docker ps List running containers
docker ps -a List all containers
docker stop container Stop (SIGTERM + SIGKILL)
docker start container Start stopped container
docker restart container Restart container
docker rm container Remove stopped container
docker rm -f container Force remove running container
docker logs -f container Follow logs
docker exec -it container bash Interactive shell in running container
Docker Containerization — Dockerfile & Docker Compose Page 47
Command Description
docker inspect container Detailed container metadata
docker stats Live resource usage
docker cp src container:/dst Copy file to container
docker cp container:/src dst Copy file from container
12.4 docker compose Commands
Command Description
docker compose up -d Create and start all services (detached)
docker compose up --build Rebuild images then start
docker compose down Stop and remove containers + networks
docker compose down -v Also remove volumes
docker compose ps List service containers
docker compose logs -f service Follow logs for a service
docker compose exec service bash Shell into running service
docker compose run --rm svc cmd One-off command in new container
docker compose build Build/rebuild images
docker compose pull Pull latest images
docker compose restart Restart all services
docker compose stop Stop without removing
docker compose config Validate and show merged config
docker compose --profile p up Start with specific profile
docker compose up --scale s=3 Scale service to 3 instances
12.5 Compose Service Keys
Key Type Description
image string Image name to use
build object Build configuration (context, dockerfile, args, target)
Docker Containerization — Dockerfile & Docker Compose Page 48
Key Type Description
command list/str Override CMD
entrypoint list/str Override ENTRYPOINT
environment list/map Environment variables
env_file list Load env vars from files
ports list Port mappings host:container
volumes list Volume mounts
networks list/map Networks to connect to
depends_on list/map Service dependencies + conditions
restart string no | always | on-failure | unless-stopped
healthcheck object Health check test and timing
deploy object Deployment config (replicas, resources)
container_name string Custom container name
labels list/map Container labels
logging object Logging driver and options
user string Run as user[:group]
working_dir string Working directory
read_only bool Read-only root filesystem
secrets list Secrets to mount
profiles list Only start with --profile
extra_hosts list Extra /etc/hosts entries
cap_add/cap_drop list Linux capabilities
security_opt list Security options (seccomp, no-new-privs)
tmpfs list tmpfs mount points
stdin_open bool -i flag
tty bool -t flag