Docker Compose Cheatsheet — Multi-Container Orchestration
Docker Compose lets you define and run multi-container Docker applications with a single YAML file. From local development to staging environments, Compose streamlines service orchestration, networking, volume management, and environment configuration. This cheatsheet covers the essential CLI commands, the compose file schema, and battle-tested patterns for dev, prod, and CI.
Quick Reference
| Scenario | Command |
| Start all services in foreground | docker compose up |
| Start all services in background (detached) | docker compose up -d |
| Rebuild images and start | docker compose up --build -d |
| Stop and remove containers, networks, volumes | docker compose down |
| Stop and remove everything including named volumes | docker compose down -v |
| View live logs from all services | docker compose logs -f |
| View logs from a specific service | docker compose logs -f web |
| List running containers | docker compose ps |
| Restart a specific service | docker compose restart web |
| Restart all services | docker compose restart |
| Execute command in a running container | docker compose exec web bash |
| Run a one-off command in a new container | docker compose run --rm web pytest |
| Pull latest images for all services | docker compose pull |
| Build or rebuild services | docker compose build |
| Check configuration validity | docker compose config |
| Scale a service to N replicas | docker compose up -d --scale worker=3 |
Docker Compose CLI Commands
### Starting & Stopping
``bash
# Start all services (foreground — Ctrl+C to stop)
docker compose up
# Start in detached (background) mode docker compose up -d
# Start specific services only docker compose up -d web db
# Rebuild images before starting (respects Dockerfile changes) docker compose up --build -d
# Stop all running services (keeps containers, can be restarted) docker compose stop
# Stop specific service docker compose stop worker
# Start stopped services
docker compose start
`
### Tearing Down
`bash
# Stop and remove containers + default networks
docker compose down
# Also remove named volumes (⚠️ data loss) docker compose down -v
# Remove everything: containers, networks, volumes, images docker compose down --rmi all -v
# Remove orphans (services not in current compose file)
docker compose down --remove-orphans
`
### Logs & Monitoring
`bash
# Stream logs from all services (tail -f style)
docker compose logs -f
# Stream logs from a single service docker compose logs -f api
# Show last N lines per service docker compose logs --tail=50
# List running containers managed by compose docker compose ps
# Show resource usage (CPU / memory)
docker compose top
`
### Restart & Exec
`bash
# Restart all services
docker compose restart
# Restart a specific service docker compose restart nginx
# Restart with a delay between stops and starts docker compose restart -t 10 web
# Open a shell inside a running container docker compose exec web sh
# Run as a different user docker compose exec -u node web npm test
# Run a one-off command (new container, removed after exit)
docker compose run --rm web python manage.py migrate
`
### Images & Build
`bash
# Pull latest images from registry
docker compose pull
# Build (or rebuild) all services docker compose build
# Build without using cache docker compose build --no-cache
# Build a specific service
docker compose build web
`
---
docker-compose.yml Structure Overview
A docker-compose.yml (or compose.yml) file has three major top-level sections.
`yaml
version: "3.9" # optional in modern compose; schema version
services: # 👈 define your containers web: build: . ports: - "8080:80" environment: - NODE_ENV=production volumes: - ./app:/app depends_on: - db
db: image: postgres:16-alpine volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_DB: myapp POSTGRES_PASSWORD: changeme
volumes: # 👈 named volumes (persistent storage) pgdata:
networks: # 👈 custom networks (optional)
frontend:
backend:
internal: true
`
### Key Service Keys
| Key | Purpose | Example |
image | Pre-built image from registry | image: nginx:alpine |
build | Build context (Dockerfile path) | build: ./app |
ports | Host-to-container port mapping | "3000:3000" |
environment | Env vars (array or object) | NODE_ENV=production |
env_file | Load env from file | env_file: .env |
volumes | Mount host paths or named volumes | ./src:/app/src |
depends_on | Startup order (services) | - db |
command | Override default CMD | npm run start |
restart | Restart policy | always, unless-stopped, on-failure |
healthcheck | Container health probe | test: ["CMD", "pg_isready"] |
networks | Attach to custom networks | - backend |
profiles | Conditional service activation | profiles: ["dev", "ci"] |
deploy | Swarm/Kubernetes deployment config | replicas: 3 |
`yaml
services:
app:
volumes:
# Named volume (managed by Docker, persistent across restarts)
- app-data:/data
# Bind mount (host directory directly mapped) - ./src:/app/src
# Anonymous volume (temporary, removed with --rm) - /tmp/cache
volumes:
app-data: # declare named volume here
`
### Networks: Custom Isolation
`yaml
services:
frontend:
networks:
- frontend
- backend # dual-homed: accessible from both
api: networks: - backend
db: networks: - backend # db only reachable from api, not frontend
networks:
frontend:
backend:
internal: true # no external (internet) access
`
---
Common Compose Patterns
### Dev / Prod Profile Pattern
Use YAML anchoring, extending, or --profile to differentiate environments. The cleanest approach is separate override files.
Base file: compose.yml
`yaml
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=${NODE_ENV:-development}
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
`
Development override: compose.dev.yml
`yaml
services:
app:
volumes:
- ./src:/app/src # live reload
environment:
- DEBUG=true
profiles: [dev]
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
profiles: [dev]
`
Production override: compose.prod.yml
`yaml
services:
app:
restart: always
environment:
- NODE_ENV=production
profiles: [prod]
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
profiles: [prod]
`
Usage:
`bash
# Dev
docker compose -f compose.yml -f compose.dev.yml up -d
# Production
docker compose -f compose.yml -f compose.prod.yml up -d
`
### Multi-Service Application (Full Stack)
`yaml
services:
# --- Frontend ---
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:8000
depends_on:
- api
volumes:
- ./frontend:/app
- /app/node_modules # anonymous volume to avoid overwriting
# --- Backend API --- api: build: ./api ports: - "8000:8000" environment: - DATABASE_URL=postgres://user:pass@db:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: db: condition: service_healthy redis: condition: service_started
# --- Database --- db: image: postgres:16-alpine volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: myapp healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d myapp"] interval: 10s timeout: 5s retries: 5 ports: - "5432:5432" # exposed only for local debugging
# --- Cache --- redis: image: redis:7-alpine volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s
# --- Background Worker --- worker: build: ./api command: celery -A tasks worker --loglevel=info environment: - DATABASE_URL=postgres://user:pass@db:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - db - redis
# --- Reverse Proxy --- nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - frontend - api
volumes:
pgdata:
redis-data:
`
### Environment Variable Management
`yaml
services:
app:
# Method 1: inline
environment:
- NODE_ENV=production
- LOG_LEVEL=info
# Method 2: from .env file (supports variable substitution) env_file: - .env - .env.production
# Method 3: using ${} substitution in compose.yml
image: myapp:${TAG:-latest}
ports:
- "${HOST_PORT:-8080}:80"
`
.env file (placed next to compose file):
`env
TAG=2.1.0
HOST_PORT=8080
POSTGRES_PASSWORD=securepassword
`
### Health Checks & Dependencies
`yaml
services:
app:
build: .
depends_on:
db:
condition: service_healthy # wait until healthcheck passes
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
start_period: 30s # give Postgres time to boot
`
### Profiles for Conditional Services
`yaml
services:
# Always runs
app:
build: .
ports:
- "3000:3000"
# Only starts with --profile debug debug-tools: image: nicolaka/netshoot profiles: [debug] depends_on: - app
# Only starts with --profile ci
selenium:
image: selenium/standalone-chrome
profiles: [ci]
`
Usage: docker compose --profile debug up or docker compose --profile ci up
### Useful One-Liners
`bash
# Restart a single service without bringing others down
docker compose restart web
# View logs and follow specific service docker compose logs -f web
# Rebuild a single service and restart it docker compose build web && docker compose up -d web
# Drop into a database container docker compose exec db psql -U postgres myapp
# Copy file into a running container docker compose cp ./init.sql db:/docker-entrypoint-initdb.d/
# Run tests in an isolated environment docker compose run --rm web pytest --cov
# Wipe everything clean (containers, volumes, images) docker compose down -v --rmi all
# Validate compose file without running anything docker compose config
# Export merged compose file (resolves all overrides) docker compose config > merged.yml ``
Compose(20)
| Command | Level | ||
|---|---|---|---|
docker compose upStart all services | Basic | docker compose up -d | |
docker compose downStop and remove all services | Basic | docker compose down | |
docker compose logsView service logs | Basic | docker compose logs -f | |
docker compose psList all service containers | Basic | docker compose ps | |
docker compose buildBuild or rebuild service images | Intermediate | docker compose build | |
docker compose execExecute a command in a running container | Intermediate | docker compose exec web bash | |
docker compose restartRestart all services | Basic | docker compose restart | |
docker compose pullPull service images | Basic | docker compose pull | |
docker compose pushPush service images to registry | Intermediate | docker compose push | |
docker compose runRun a one-off command | Intermediate | docker compose run web env | |
docker compose stopStop all services without removing containers | Basic | docker compose stop | |
docker compose rmRemove stopped service containers | Intermediate | docker compose rm -f | |
docker compose topDisplay running processes | Intermediate | docker compose top | |
docker compose configValidate and view Compose configuration | Intermediate | docker compose config | |
docker compose imagesList images used by services | Basic | docker compose images | |
docker compose createCreate service containers without starting | Intermediate | docker compose create | |
docker compose eventsMonitor real-time events from containers | Expert | docker compose events --json | |
docker compose killForce stop service containers | Intermediate | docker compose kill web | |
docker compose portPrint the public port for a service container | Intermediate | docker compose port web 3000 | |
docker compose versionShow Docker Compose version information | Basic | docker compose version |