Docker Compose: The Complete Guide
Docker Compose lets you define and run multi-container Docker applications with a single command.
What Is Docker Compose?
Instead of running multiple docker run commands:
docker run -d --name db postgres:16
docker run -d --name api --link db my-api
docker run -d --name frontend --link api nginxYou define everything in a YAML file:
# docker-compose.yml
services:
db:
image: postgres:16
api:
build: ./api
depends_on:
- db
frontend:
build: ./frontend
ports:
- "80:80"And start everything with one command: docker compose up.
Basic Structure
version: "3.9" # optional in newer Docker Compose
services:
# Define your containers here
networks:
# Define custom networks
volumes:
# Define persistent volumesDefining Services
services:
web:
build: . # build from Dockerfile
image: my-app:latest # or use existing image
ports:
- "8000:8000" # host:container
environment:
- DEBUG=true
- DB_HOST=db
env_file:
- .env
volumes:
- ./src:/app/src # bind mount
- app_data:/data # named volume
depends_on:
- db
- redis
restart: unless-stoppedDocker Compose in Action
Development
services:
api:
build:
context: ./api
target: development
volumes:
- ./api:/app # live reload
environment:
- DEBUG=1
ports:
- "8000:8000"
command: uvicorn app:app --reload --host 0.0.0.0Production
services:
api:
build:
context: ./api
target: production
ports:
- "8000:8000"
environment:
- DEBUG=0
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3Complete Example
services:
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:7-alpine
api:
build: ./api
depends_on:
- db
- redis
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/myapp
REDIS_URL: redis://redis:6379
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- api
volumes:
pgdata:Common Commands
docker compose up # create and start
docker compose up -d # in background
docker compose down # stop and remove
docker compose down -v # remove volumes too
docker compose logs # view logs
docker compose logs -f api # follow logs for a service
docker compose ps # list running services
docker compose exec api bash # shell into a service
docker compose build # rebuild images
docker compose pull # pull latest images
docker compose restart # restart all services
docker compose restart api # restart a single serviceMultiple Environments
docker compose -f docker-compose.yml -f docker-compose.prod.yml up# docker-compose.override.yml (auto-loaded for development)
services:
api:
volumes:
- ./api:/app
environment:
- DEBUG=1# docker-compose.prod.yml
services:
api:
restart: always
environment:
- DEBUG=0Networks
services:
api:
networks:
- frontend
- backend
db:
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridgeBest Practices
- Use
.envfiles — never hardcode secrets in docker-compose.yml - Specify restart policies —
unless-stoppedfor production - Use healthchecks — ensures services are actually ready
- Pin image versions —
postgres:16-alpine, notpostgres:latest - Keep dev and prod close — minimize differences between environments
- Use
.dockerignore— don’t send unnecessary files to the builder
Related: Start with Docker for beginners and learn Dockerfile best practices.