Docker Compose
Define and run multi-container apps with docker-compose.yml — services, volumes, networks
Knowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
Docker Compose lets you define a multi-container application (app + database + cache + worker) in a single docker-compose.yml and manage it with one command.
Minimal example — app + Postgres:
yaml # docker-compose.yml services: web: build: . # build from local Dockerfile ports: - "8000:8000" environment: DATABASE_URL: postgresql://postgres:secret@db:5432/mydb depends_on: - db db: image: postgres:16 environment: POSTGRES_PASSWORD: secret POSTGRES_DB: mydb volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata: # named volume — persists across container restarts
Essential Compose commands:
bash docker compose up # start all services (foreground) docker compose up -d # detached (background) docker compose down # stop and remove containers docker compose down -v # also remove volumes docker compose logs -f web # stream logs for the web service docker compose ps # show status of all services docker compose exec web bash # shell inside running web container docker compose build # rebuild images without starting
Volumes — two types:
yaml volumes: - pgdata:/var/lib/postgresql/data # named volume: Docker manages it - ./src:/app/src # bind mount: host path:container path `` Bind mounts are ideal for development — code changes on your host instantly reflect inside the container without a rebuild.
Environment variables — prefer .env file:
bash # .env (in the same directory as docker-compose.yml) POSTGRES_PASSWORD=secret APP_PORT=8000 yaml services: web: ports: - "${APP_PORT}:8000" Compose auto-loads .env — never commit .env` to version control.
depends_on and health checks:
yaml services: web: depends_on: db: condition: service_healthy # wait until db is truly ready db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s retries: 5 depends_on` alone only waits for the container to start, not for the app inside to be ready. Combine with a healthcheck for databases.
Examples
Full dev stack: FastAPI + Postgres + Redis
Services reference each other by service name (db, cache) — Compose sets up an internal DNS so `db:5432` resolves correctly
# docker-compose.yml
services:
api:
build: .
ports:
- "8000:8000"
volumes:
- ./src:/app/src # live reload: code changes reflected instantly
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/appdb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
cache:
image: redis:7-alpine
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:Dev vs prod compose files
docker-compose.override.yml is auto-merged in dev; in CI you explicitly pick which files to combine with `-f`
# docker-compose.yml (base)
services:
web:
image: myapp:latest
environment:
APP_ENV: production
# docker-compose.override.yml (dev overrides — auto-loaded)
services:
web:
build: . # use local build in dev, not prod image
volumes:
- .:/app # live reload
environment:
APP_ENV: development
DEBUG: "true"
ports:
- "8000:8000"
# CI/prod usage (explicit override file)
# docker compose -f docker-compose.yml -f docker-compose.prod.yml upHow well did you understand this?
Next in Docker & Containers
Docker Networking