Chapter 4: Docker Compose
Authored by syscook.dev
What is Docker Compose?
Docker Compose is a tool for defining and running multi-container Docker applications. It uses YAML files to configure your application's services, networks, and volumes, making it easy to manage complex applications with multiple containers.
Key Concepts:
- Service: A container that runs as part of your application
- Network: Communication between services
- Volume: Persistent data storage
- Environment: Configuration through environment variables
- Dependencies: Service startup order and relationships
- Scaling: Running multiple instances of a service
Why Use Docker Compose?
1. Simplify Multi-Container Management
Compose makes it easy to manage applications with multiple services.
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
- redis
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
redis:
image: redis:alpine
Benefits:
- Single command to start all services
- Automatic service discovery
- Shared networking and volumes
- Easy scaling and updates
2. Environment Consistency
Compose ensures consistent environments across development, staging, and production.
# Development environment
version: '3.8'
services:
app:
build: .
volumes:
- .:/app
environment:
- NODE_ENV=development
ports:
- "3000:3000"
3. Easy Service Orchestration
Compose handles service dependencies, networking, and data persistence automatically.
# Production environment
version: '3.8'
services:
web:
image: myapp:latest
depends_on:
- db
- cache
environment:
- NODE_ENV=production
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
cache:
image: redis:alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Docker Compose File Structure
1. Basic Compose File
Minimal Configuration
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
Service Configuration Options
version: '3.8'
services:
web:
# Image or build
image: nginx:alpine
# build: .
# Container name
container_name: my-web
# Port mapping
ports:
- "80:80"
- "443:443"
# Environment variables
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80
# env_file: .env
# Volume mounts
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./html:/usr/share/nginx/html
# Network configuration
networks:
- frontend
# Dependencies
depends_on:
- api
# Restart policy
restart: unless-stopped
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
2. Advanced Configuration
Multi-Environment Setup
# docker-compose.yml (base)
version: '3.8'
services:
web:
image: ${IMAGE_NAME:-myapp}:${IMAGE_TAG:-latest}
ports:
- "${WEB_PORT:-3000}:3000"
environment:
- NODE_ENV=${NODE_ENV:-development}
depends_on:
- db
db:
image: postgres:${POSTGRES_VERSION:-13}
environment:
- POSTGRES_DB=${POSTGRES_DB:-myapp}
- POSTGRES_USER=${POSTGRES_USER:-user}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Environment-Specific Files
# docker-compose.override.yml (development)
version: '3.8'
services:
web:
build: .
volumes:
- .:/app
environment:
- NODE_ENV=development
- DEBUG=true
ports:
- "3000:3000"
- "9229:9229" # Debug port
# docker-compose.prod.yml (production)
version: '3.8'
services:
web:
image: myapp:latest
environment:
- NODE_ENV=production
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: '0.5'
restart: unless-stopped
Service Configuration Deep Dive
1. Build Configuration
Build from Dockerfile
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
args:
- NODE_ENV=production
target: production
ports:
- "3000:3000"
Build with Multiple Dockerfiles
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile.web
api:
build:
context: .
dockerfile: Dockerfile.api
2. Networking Configuration
Custom Networks
version: '3.8'
services:
web:
image: nginx:alpine
networks:
- frontend
api:
image: myapp:latest
networks:
- frontend
- backend
db:
image: postgres:13
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
External Networks
version: '3.8'
services:
web:
image: nginx:alpine
networks:
- external_network
networks:
external_network:
external: true
name: my_external_network
3. Volume Configuration
Named Volumes
version: '3.8'
services:
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
web:
image: nginx:alpine
volumes:
- static_files:/usr/share/nginx/html
volumes:
postgres_data:
driver: local
static_files:
driver: local
Bind Mounts
version: '3.8'
services:
web:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html
- /var/log/nginx:/var/log/nginx
4. Environment Configuration
Environment Variables
version: '3.8'
services:
web:
image: myapp:latest
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
env_file:
- .env
- .env.production
Environment File
# .env
NODE_ENV=production
DATABASE_URL=postgresql://user:password@db:5430/myapp
REDIS_URL=redis://redis:6379
WEB_PORT=3000
POSTGRES_DB=myapp
POSTGRES_USER=user
POSTGRES_PASSWORD=password
Advanced Compose Features
1. Service Dependencies
Startup Order Control
version: '3.8'
services:
web:
image: myapp:latest
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:alpine
Health Checks
version: '3.8'
services:
web:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
2. Service Scaling
Scale Services
# Scale web service to 3 instances
docker compose up --scale web=3
# Scale multiple services
docker compose up --scale web=3 --scale api=2
Load Balancing with Nginx
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
web:
image: myapp:latest
deploy:
replicas: 3
3. Secrets Management
Docker Secrets
version: '3.8'
services:
web:
image: myapp:latest
secrets:
- db_password
- api_key
db:
image: postgres:13
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
External Secrets
version: '3.8'
services:
web:
image: myapp:latest
secrets:
- db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
external: true
name: myapp_db_password
Production Deployment
1. Production Compose File
Production-Ready Configuration
# docker-compose.prod.yml
version: '3.8'
services:
web:
image: myapp:latest
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
environment:
- NODE_ENV=production
networks:
- frontend
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- web
networks:
- frontend
restart: unless-stopped
db:
image: postgres:13
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
networks:
- backend
secrets:
- db_password
restart: unless-stopped
redis:
image: redis:alpine
volumes:
- redis_data:/data
networks:
- backend
restart: unless-stopped
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
postgres_data:
driver: local
redis_data:
driver: local
secrets:
db_password:
external: true
2. Deployment Strategies
Blue-Green Deployment
# docker-compose.blue.yml
version: '3.8'
services:
web-blue:
image: myapp:v1.0
ports:
- "3000:3000"
networks:
- blue
networks:
blue:
driver: bridge
# docker-compose.green.yml
version: '3.8'
services:
web-green:
image: myapp:v1.1
ports:
- "3001:3000"
networks:
- green
networks:
green:
driver: bridge
Rolling Updates
# Update service with rolling restart
docker compose up -d --no-deps web
# Update with new image
docker compose pull web
docker compose up -d web
Common Use Cases
1. Full-Stack Application
React + Node.js + PostgreSQL
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:5000
depends_on:
- backend
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/myapp
depends_on:
- db
- redis
db:
image: postgres:13
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
2. Microservices Architecture
API Gateway + Services
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api-gateway
api-gateway:
image: myapp/api-gateway:latest
environment:
- USER_SERVICE_URL=http://user-service:3001
- ORDER_SERVICE_URL=http://order-service:3002
depends_on:
- user-service
- order-service
user-service:
image: myapp/user-service:latest
environment:
- DATABASE_URL=postgresql://user:password@user-db:5432/users
depends_on:
- user-db
order-service:
image: myapp/order-service:latest
environment:
- DATABASE_URL=postgresql://user:password@order-db:5432/orders
depends_on:
- order-db
user-db:
image: postgres:13
environment:
- POSTGRES_DB=users
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- user_data:/var/lib/postgresql/data
order-db:
image: postgres:13
environment:
- POSTGRES_DB=orders
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- order_data:/var/lib/postgresql/data
volumes:
user_data:
order_data:
3. Development Environment
Hot Reload Development
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=true
command: npm run dev
db:
image: postgres:13
environment:
- POSTGRES_DB=myapp_dev
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
volumes:
postgres_dev_data:
Compose Commands
1. Basic Commands
Service Management
# Start services
docker compose up
# Start in background
docker compose up -d
# Start specific services
docker compose up web db
# Stop services
docker compose down
# Stop and remove volumes
docker compose down -v
# Restart services
docker compose restart
# Restart specific service
docker compose restart web
Service Information
# List services
docker compose ps
# View logs
docker compose logs
# View logs for specific service
docker compose logs web
# Follow logs
docker compose logs -f web
# View service status
docker compose top
2. Advanced Commands
Service Scaling
# Scale services
docker compose up --scale web=3
# Scale specific service
docker compose up --scale web=3 --scale api=2
Service Updates
# Pull latest images
docker compose pull
# Rebuild and start
docker compose up --build
# Force recreate containers
docker compose up --force-recreate
Service Execution
# Execute command in service
docker compose exec web bash
# Execute command as root
docker compose exec --user root web bash
# Run one-time command
docker compose run web npm test
Troubleshooting
1. Common Issues
Service Dependencies
# Check service status
docker compose ps
# View service logs
docker compose logs web
# Check service health
docker compose exec web curl -f http://localhost:3000/health
Network Issues
# List networks
docker network ls
# Inspect network
docker network inspect myapp_default
# Test connectivity
docker compose exec web ping db
Volume Issues
# List volumes
docker volume ls
# Inspect volume
docker volume inspect myapp_postgres_data
# Remove volumes
docker compose down -v
2. Debugging Commands
Service Debugging
# View service configuration
docker compose config
# Validate compose file
docker compose config --quiet
# View service logs with timestamps
docker compose logs -t web
# View service resource usage
docker compose top
Summary
Docker Compose is a powerful tool for managing multi-container applications:
- Simplifies orchestration of complex applications with multiple services
- Provides consistent environments across development, staging, and production
- Enables easy scaling and service management
- Handles networking and storage automatically
- Supports advanced features like health checks, secrets, and scaling
- Integrates with Docker Swarm for production orchestration
By mastering Docker Compose, you can efficiently manage complex applications and deploy them consistently across different environments.
This tutorial is part of the SysCook DevOps series. Continue to the next chapter to learn about Docker networking and storage.