Docker Compose for Multi-Service
Define and run multi-container apps with docker-compose.yml for dev and small prod.
ContainersIntermediate
Why
Docker Compose lets you define multi-service apps (web + db + cache + worker) in one YAML file. One command brings up the entire stack with networking and volumes.
Docker is used by 59% of professional developers per the Stack Overflow 2024 survey, and Kubernetes runs 60% of containerized workloads in production (CNCF 2024 survey). Container orchestration reduces deployment-related incidents by 35% through declarative rollouts.
When
Use Compose for local dev, CI, and small production. Avoid for large-scale prod — use K8s/Swarm.
How
1. Write — docker-compose.yml with services, networks, volumes
2. Up — docker compose up -d (detached)
3. Down — docker compose down -v (remove volumes)
4. Logs — docker compose logs -f service
5. Exec — docker compose exec service sh
6. Build — docker compose build
7. Override — docker-compose.override.yml for dev
Example
# docker-compose.yml
services:
web:
build: .
ports: ["3000:3000"]
environment:
DATABASE_URL: postgres://user:pass@db:5432/app
depends_on: [db, redis]
db:
image: postgres:16
volumes: [pgdata:/var/lib/postgresql/data]
environment: { POSTGRES_PASSWORD: pass }
redis: { image: redis:7 }
worker:
build: .
command: npm run worker
depends_on: [redis]
volumes: { pgdata: }
# Commands
docker compose up -d # start all
docker compose logs -f web # follow web logs
docker compose exec db psql -U user app # psql in db
Common Problems
- ⚠Services can't resolve each other — must be on same network, Compose creates default network
- ⚠Data lost on down — volumes not persisted, use named volumes not bind mounts
- ⚠Configuration drift between environments — use infrastructure-as-code (Terraform/Pulumi) to keep dev/staging/prod identical
Best Practices
- ✓Use named volumes for data — persists across compose down/up
- ✓Use depends_on with healthcheck — waits for DB ready before app starts
- ✓Set up health checks and graceful shutdown — handle SIGTERM to drain in-flight requests before exit