Docs
/
Docker Kubernetes
Chapter 6
06 — Docker Compose
What is Docker Compose?
Define and run multi-container applications with a single YAML file.
# Instead of running 5 separate docker run commands:
docker compose up -d # Start everything
docker compose down # Stop everything
Basic Example
# docker-compose.yml
services:
api:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
Commands
docker compose up -d # Start all services (detached)
docker compose up -d --build # Rebuild images and start
docker compose down # Stop and remove containers
docker compose down -v # + remove volumes
docker compose ps # List running services
docker compose logs # View all logs
docker compose logs -f api # Follow specific service logs
docker compose exec api sh # Shell into service
docker compose restart api # Restart one service
docker compose pull # Pull latest images
docker compose build # Rebuild images
docker compose stop # Stop without removing
docker compose start # Start stopped services
Build Configuration
services:
api:
build:
context: . # Build context path
dockerfile: Dockerfile.prod # Custom Dockerfile
target: production # Multi-stage target
args:
NODE_ENV: production # Build arguments
image: my-app:latest # Tag the built image
Networking
services:
api:
networks:
- frontend
- backend
web:
networks:
- frontend # Can reach api, NOT db
db:
networks:
- backend # Can reach api, NOT web
networks:
frontend:
backend:
By default, all services share one network and can reach each other by service name.
Environment Variables
services:
api:
# Inline
environment:
- NODE_ENV=production
- PORT=3000
# From .env file
env_file:
- .env
- .env.local
# From host environment
environment:
- API_KEY # Passes host's $API_KEY into container
# .env file (auto-loaded by Compose)
POSTGRES_PASSWORD=secret
API_KEY=abc123
Profiles
Run subsets of services for different scenarios.
services:
api:
build: .
ports: ["3000:3000"]
db:
image: postgres:16-alpine
redis:
image: redis:7-alpine
# Only runs with --profile debug
adminer:
image: adminer
ports: ["8080:8080"]
profiles: [debug]
# Only runs with --profile monitoring
prometheus:
image: prom/prometheus
profiles: [monitoring]
docker compose up -d # api + db + redis
docker compose --profile debug up -d # + adminer
docker compose --profile monitoring up -d # + prometheus
Override Files
# docker-compose.yml (base)
services:
api:
image: my-app:latest
ports: ["3000:3000"]
# docker-compose.override.yml (dev — auto-merged)
services:
api:
build: .
volumes:
- ./src:/app/src
environment:
- DEBUG=true
# docker-compose.prod.yml (production)
services:
api:
restart: always
deploy:
replicas: 3
# Dev (auto-merges override)
docker compose up -d
# Production (explicit file)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Full-Stack Example
services:
frontend:
build: ./frontend
ports: ["3000:3000"]
depends_on: [api]
api:
build: ./backend
ports: ["4000:4000"]
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/app
REDIS_URL: redis://cache:6379
JWT_SECRET: my-secret
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postgres
interval: 5s
retries: 5
cache:
image: redis:7-alpine
worker:
build: ./backend
command: node dist/worker.js
environment:
REDIS_URL: redis://cache:6379
depends_on: [cache]
volumes:
pgdata:
Key Takeaways
docker-compose.ymldefines your entire stack in one file- Services communicate by name (e.g.,
db,cache,api) - Use
depends_onwithcondition: service_healthyfor startup ordering - Use volumes for persistent data, bind mounts for dev hot-reload
- Use profiles to include optional services (debug tools, monitoring)
- Use override files for environment-specific config (dev vs prod)
docker compose up -d --buildis the most common dev command