0% found this document useful (0 votes)
6 views48 pages

Docker Containerization

This document serves as a comprehensive guide to mastering Docker, covering essential concepts such as containerization, Dockerfiles, and Docker Compose. It explains the differences between containers and virtual machines, outlines core Docker components, and provides practical commands for managing images and containers. Additionally, it details the structure and instructions of a Dockerfile, emphasizing best practices for building efficient Docker images.

Uploaded by

George
Copyright
© Public Domain
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views48 pages

Docker Containerization

This document serves as a comprehensive guide to mastering Docker, covering essential concepts such as containerization, Dockerfiles, and Docker Compose. It explains the differences between containers and virtual machines, outlines core Docker components, and provides practical commands for managing images and containers. Additionally, it details the structure and instructions of a Dockerfile, emphasizing best practices for building efficient Docker images.

Uploaded by

George
Copyright
© Public Domain
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

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

You might also like