3. Docker Compose Orchestration#
flowchart TB
subgraph compose["docker-compose.yml"]
direction TB
web[Web App<br/>:8080] --> db[(MySQL<br/>:3306)]
web --> cache[(Redis<br/>:6379)]
web --> mq[RabbitMQ<br/>:5672]
nginx[Nginx<br/>:80/:443] --> web
end
subgraph volumes["Volumes"]
db_data[(db_data)]
redis_data[(redis_data)]
end
db --- db_data
cache --- redis_data
Compose File Structure#
# docker-compose.yml
services:
web:
build: .
ports:
- "8080:8080"
environment:
- DB_HOST=db
- REDIS_HOST=cache
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- backend
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpass}
MYSQL_DATABASE: mydb
MYSQL_USER: app
MYSQL_PASSWORD: ${DB_PASSWORD:-apppass}
volumes:
- db_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
cache:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- backend
volumes:
db_data:
redis_data:
networks:
backend:
driver: bridge
Essential Commands#
# Start all services (detached)
docker compose up -d
# Start with build
docker compose up -d --build
# Stop all services
docker compose down
# Stop and remove volumes
docker compose down -v
# View logs
docker compose logs -f web
# Scale a service
docker compose up -d --scale web=3
# Execute command in a service
docker compose exec db mysql -u root -p
# List running services
docker compose ps
# Restart a single service
docker compose restart web
# Pull latest images
docker compose pull
Environment Variables#
.env file#
# .env (auto-loaded by docker compose)
DB_ROOT_PASSWORD=supersecret
DB_PASSWORD=apppass
APP_PORT=8080
COMPOSE_PROJECT_NAME=myproject
Override files#
# docker-compose.override.yml (auto-loaded for local dev)
services:
web:
build:
context: .
target: development
volumes:
- .:/app # hot reload
environment:
- DEBUG=true
# docker-compose.prod.yml
services:
web:
image: registry.example.com/myapp:${VERSION}
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
# Use: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Profiles#
services:
web:
image: myapp:latest
# always started
debug-tools:
image: nicolaka/netshoot
profiles: ["debug"]
# only started with: docker compose --profile debug up
test-runner:
image: myapp-test:latest
profiles: ["test"]
docker compose --profile debug up -d
docker compose --profile test run test-runner
LNMP Stack Example#
# docker-compose-lnmp.yml
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- static_files:/var/www/static:ro
depends_on:
- app
networks:
- frontend
- backend
app:
build: ./app
expose:
- "8000"
environment:
- DATABASE_URL=mysql+pymysql://app:apppass@db:3306/mydb
volumes:
- static_files:/app/static
depends_on:
db:
condition: service_healthy
networks:
- backend
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: mydb
MYSQL_USER: app
MYSQL_PASSWORD: apppass
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
phpmyadmin:
image: phpmyadmin:5
ports:
- "8080:80"
environment:
PMA_HOST: db
depends_on:
- db
profiles: ["admin"]
networks:
- backend
volumes:
mysql_data:
static_files:
networks:
frontend:
backend:
Microservice Example#
services:
gateway:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./gateway/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- user-service
- order-service
user-service:
build: ./services/user
environment:
- DB_HOST=user-db
depends_on:
user-db:
condition: service_healthy
order-service:
build: ./services/order
environment:
- DB_HOST=order-db
- USER_SERVICE_URL=http://user-service:8080
depends_on:
order-db:
condition: service_healthy
user-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: users
POSTGRES_PASSWORD: secret
volumes:
- user_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
order-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: orders
POSTGRES_PASSWORD: secret
volumes:
- order_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
volumes:
user_db_data:
order_db_data:
Tips#
Use
depends_onwithcondition: service_healthyfor reliable startup orderUse named volumes for persistent data, bind mounts for development
Use
.envfor secrets, never commit them to gitUse profiles to separate dev/debug/test tools
Use
docker compose configto validate and view the merged config