Docker Compose
Orchestrate multi-container applications with declarative YAML configuration.
When to Use This Skill
Use this skill when:
-
Running multi-container applications locally
-
Setting up development environments
-
Defining service dependencies and networking
-
Managing application stacks with multiple services
-
Creating reproducible development setups
Prerequisites
-
Docker Engine with Compose plugin (v2)
-
Basic Docker knowledge
-
YAML syntax understanding
Basic Configuration
Simple Application Stack
docker-compose.yml
version: '3.8'
services: web: build: . ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgres://postgres:secret@db:5432/myapp depends_on: - db - redis
db: image: postgres:15-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: secret POSTGRES_DB: myapp volumes: - postgres-data:/var/lib/postgresql/data ports: - "5432:5432"
redis: image: redis:7-alpine ports: - "6379:6379"
volumes: postgres-data:
Service Configuration
Build Options
services: app: build: context: ./app dockerfile: Dockerfile.dev args: NODE_VERSION: "20" target: development cache_from: - myapp:cache image: myapp:dev
Environment Variables
services: app: environment: - NODE_ENV=production - API_KEY=${API_KEY} # From shell or .env file env_file: - .env - .env.local
Port Mapping
services: web: ports: - "3000:3000" # HOST:CONTAINER - "127.0.0.1:9229:9229" # Bind to localhost only - "8080-8090:8080-8090" # Port range expose: - "3000" # Internal only (no host binding)
Volume Mounts
services: app: volumes: # Named volume - app-data:/app/data # Bind mount - ./src:/app/src # Read-only bind mount - ./config:/app/config:ro # Anonymous volume (for node_modules) - /app/node_modules
volumes: app-data: driver: local
Dependencies
services: web: depends_on: db: condition: service_healthy redis: condition: service_started
db: image: postgres:15 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5
Networking
Custom Networks
services: frontend: networks: - frontend-net
backend: networks: - frontend-net - backend-net
db: networks: - backend-net
networks: frontend-net: driver: bridge backend-net: driver: bridge internal: true # No external access
Network Aliases
services: db: networks: backend: aliases: - database - postgres
networks: backend:
Resource Limits
services: app: deploy: resources: limits: cpus: '2' memory: 1G reservations: cpus: '0.5' memory: 256M
Multiple Compose Files
Override Files
docker-compose.yml (base)
services: web: image: myapp:latest ports: - "3000:3000"
docker-compose.override.yml (development - auto-loaded)
services: web: build: . volumes: - ./src:/app/src environment: - DEBUG=true
docker-compose.prod.yml (production)
services: web: deploy: replicas: 3 environment: - DEBUG=false
Using Multiple Files
Development (uses override automatically)
docker compose up
Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
Merge and view final config
docker compose -f docker-compose.yml -f docker-compose.prod.yml config
Profiles
services: web: image: myapp
db: image: postgres:15
debug: image: busybox profiles: - debug
monitoring: image: prometheus profiles: - monitoring
Run without profiles (web, db only)
docker compose up
Run with debug profile
docker compose --profile debug up
Run with multiple profiles
docker compose --profile debug --profile monitoring up
Commands
Lifecycle
Start services
docker compose up -d
Start specific service
docker compose up -d web
Stop services
docker compose stop
Stop and remove containers
docker compose down
Stop and remove everything including volumes
docker compose down -v --rmi all
Restart services
docker compose restart web
Building
Build images
docker compose build
Build without cache
docker compose build --no-cache
Build and start
docker compose up --build
Pull latest images
docker compose pull
Monitoring
View logs
docker compose logs -f
View specific service logs
docker compose logs -f web
View running services
docker compose ps
View resource usage
docker compose top
Execution
Run command in new container
docker compose run --rm web npm test
Execute in running container
docker compose exec web /bin/sh
Scale service
docker compose up -d --scale worker=3
Development Workflow
Watch Mode (Compose v2.22+)
services: web: build: . develop: watch: - action: sync path: ./src target: /app/src - action: rebuild path: ./package.json
docker compose watch
Hot Reload Setup
services: web: build: context: . target: development volumes: - ./src:/app/src - /app/node_modules environment: - CHOKIDAR_USEPOLLING=true command: npm run dev
Common Patterns
Database Initialization
services: db: image: postgres:15 volumes: - postgres-data:/var/lib/postgresql/data - ./init-scripts:/docker-entrypoint-initdb.d:ro environment: POSTGRES_DB: myapp
Reverse Proxy
services: proxy: image: traefik:v3.0 ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik.yml:/etc/traefik/traefik.yml:ro
web:
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(app.localhost)"
Common Issues
Issue: Container Cannot Resolve Service Name
Problem: Service can't connect to another service by name Solution: Ensure services are on the same network, check depends_on
Issue: Volume Permissions
Problem: Container can't write to mounted volume Solution: Match container user UID with host, or use named volumes
Issue: Port Already in Use
Problem: Error binding to port Solution: Change host port or stop conflicting service
Issue: Changes Not Reflected
Problem: Code changes don't appear in container Solution: Check volume mounts, rebuild if Dockerfile changed
Best Practices
-
Use named volumes for persistent data
-
Define healthchecks for database dependencies
-
Use profiles to separate optional services
-
Keep secrets in .env files (not committed)
-
Use override files for environment-specific config
-
Pin image versions for reproducibility
-
Use networks to isolate service groups
-
Leverage watch mode for development
Related Skills
-
docker-management - Docker fundamentals
-
kubernetes-ops - Production orchestration
-
reverse-proxy - Production routing