Docker Compose
Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. Then, with a single command, you create and start all the services from your configuration.
What is Docker Compose?
Docker Compose allows you to:
- Define multi-container applications in a single YAML file
- Manage application lifecycle with simple commands
- Create isolated environments for development, testing, and production
- Scale services up or down as needed
- Share configurations across teams and environments
Key Concepts
- Services: Define how containers should run
- Networks: Control how containers communicate
- Volumes: Manage persistent data storage
- Environment Variables: Configure application settings
- Dependencies: Define startup order and relationships
Basic Docker Compose File Structure
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
networks:
- frontend
database:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
volumes:
db_data:
networks:
frontend:
backend:
Common Commands
Basic Operations
# Start services in background
docker-compose up -d
# View running services
docker-compose ps
# View logs
docker-compose logs
# Stop services
docker-compose down
# Stop and remove volumes
docker-compose down -v
Development Commands
# Build services
docker-compose build
# Rebuild without cache
docker-compose build --no-cache
# Run one-time commands
docker-compose exec web bash
# Scale services
docker-compose up -d --scale web=3
Container Management
Tip
Pin images to specific tags in your docker-compose file. This prevents surprise failures due to automatic updates and ensures consistent deployments across environments.
Update All Containers
The following process safely updates all containers while minimizing downtime:
# Step 1: Pull latest images
docker-compose pull
# Step 2: Create backup of current state (optional)
docker-compose ps > containers_backup.txt
# Step 3: Update containers with zero-downtime strategy
docker-compose up --force-recreate --build -d
# Step 4: Verify services are running
docker-compose ps
# Step 5: Clean up unused images
docker image prune -f
Safe Update Process with Rollback
# Create a backup script for safe updates
#!/bin/bash
# Backup current compose file
cp docker-compose.yml docker-compose.yml.backup
# Pull new images
docker-compose pull
# Test configuration
docker-compose config
# Update with health checks
docker-compose up -d --remove-orphans
# Wait for services to be healthy
sleep 30
# Check if all services are running
if docker-compose ps | grep -q "Exit"; then
echo "Some services failed, rolling back..."
docker-compose down
cp docker-compose.yml.backup docker-compose.yml
docker-compose up -d
exit 1
fi
echo "Update successful!"
docker image prune -f
Update Single Service
# Update specific service
docker-compose pull service_name
docker-compose up -d --no-deps service_name
# Force recreate single service
docker-compose up -d --force-recreate service_name
Best Practices
Security
# Use non-root users
services:
web:
image: nginx:alpine
user: "1000:1000"
app:
build: .
user: "appuser"
read_only: true
tmpfs:
- /tmp
- /var/cache
Environment Variables
# Use .env file for sensitive data
services:
database:
image: postgres:13
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Health Checks
services:
web:
image: nginx:alpine
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Resource Limits
services:
web:
image: nginx:alpine
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Example Configurations
Web Application Stack
# Full web application with database and cache
version: '3.8'
services:
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
web:
build: .
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
networks:
- frontend
- backend
db:
image: postgres:13-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
secrets:
- db_password
networks:
- backend
redis:
image: redis:6-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- backend
volumes:
postgres_data:
redis_data:
networks:
frontend:
backend:
secrets:
db_password:
file: ./secrets/db_password.txt
Development Environment
# Development setup with hot reload
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
command: npm run dev
db:
image: postgres:13-alpine
ports:
- "5432:5432"
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: devpass
volumes:
- dev_db_data:/var/lib/postgresql/data
volumes:
dev_db_data:
Troubleshooting
Common Issues
# Check service logs
docker-compose logs service_name
# Inspect service configuration
docker-compose config
# Check resource usage
docker stats $(docker-compose ps -q)
# Debug networking
docker network ls
docker network inspect project_network_name
# Clean up everything
docker-compose down -v --remove-orphans
docker system prune -af
Performance Monitoring
# Monitor resource usage
docker-compose top
# View real-time logs
docker-compose logs -f
# Check disk usage
docker system df
Migration and Backup
Backup Strategy
#!/bin/bash
# backup-compose.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="./backups/$DATE"
# Create backup directory
mkdir -p $BACKUP_DIR
# Export volumes
docker-compose exec -T db pg_dump -U user myapp > $BACKUP_DIR/database.sql
# Backup compose files
cp docker-compose.yml $BACKUP_DIR/
cp .env $BACKUP_DIR/ 2>/dev/null || true
# Archive volumes
docker run --rm -v project_db_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/volumes.tar.gz -C /data .
echo "Backup completed: $BACKUP_DIR"
Restore Process
#!/bin/bash
# restore-compose.sh
BACKUP_DIR=$1
if [ -z "$BACKUP_DIR" ]; then
echo "Usage: $0 <backup_directory>"
exit 1
fi
# Stop services
docker-compose down
# Restore configuration
cp $BACKUP_DIR/docker-compose.yml .
cp $BACKUP_DIR/.env . 2>/dev/null || true
# Start database
docker-compose up -d db
# Wait for database
sleep 10
# Restore database
docker-compose exec -T db psql -U user myapp < $BACKUP_DIR/database.sql
# Start all services
docker-compose up -d
echo "Restore completed from: $BACKUP_DIR"