Docker Compose Cheat Sheet β Complete Reference Guide (2025)
Introduction: What is Docker Compose?
Docker Compose is a powerful tool that allows you to define and run multi-container Docker applications with a single command. Instead of manually running multiple docker run commands for each service, you write a declarative docker-compose.yml configuration file that describes your entire application stack.
Why Use Docker Compose?
- Simplified Multi-Container Orchestration: Manage complex applications with multiple services (web servers, databases, caches) effortlessly
- Environment Consistency: Ensure development, staging, and production environments use identical configurations
- Faster Development: Spin up entire development environments with a single command
- Service Networking: Automatic DNS resolution between containersβno port mapping headaches
- Volume Management: Persist data across container restarts with named volumes
- Environment Variables: Manage configuration centrally with .env files
- Infrastructure as Code: Version control your entire application stack
Docker Compose is perfect for development, testing, and small production deployments. For large-scale deployments, Kubernetes is recommended.
Installation & Quick Reference
Install Docker Compose (v2+)
Docker Compose v2+ comes bundled with Docker Desktop and Docker Engine on Linux.
Verify Installation
# Check version
docker compose version
# Should output: Docker Compose version 2.x.x
Install on Linux (if not included)
curl -L https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker compose version
docker-compose.yml Structure & YAML Reference
The docker-compose.yml file is the blueprint for your entire application. Here's the complete structure with all available options:
Basic File Structure
version: '3.9'
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
networks:
- frontend
api:
build:
context: ./app
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=postgres
depends_on:
- postgres
networks:
- backend
postgres:
image: postgres:15
environment:
- POSTGRES_PASSWORD=secret123
volumes:
- db_data:/var/lib/postgresql/data
networks:
- backend
volumes:
db_data:
networks:
frontend:
backend:
Complete Service Configuration Reference
| Property | Type | Description | Example |
|---|---|---|---|
image |
String | Docker image to use | nginx:latest |
build |
Object/String | Build image from Dockerfile | ./app or {context: ./app, dockerfile: Dockerfile} |
ports |
Array | Expose ports: "host:container" | ["8080:80", "5432:5432"] |
environment |
Object/Array | Environment variables | NODE_ENV: production or array format |
volumes |
Array | Mount volumes: "host:container:mode" | ["./data:/data", "db_vol:/var/lib/postgresql"] |
depends_on |
Array/Object | Service dependencies | - postgres or {postgres: {condition: service_healthy}} |
restart |
String | Restart policy | no, always, on-failure, unless-stopped |
command |
String/Array | Override default command | python app.py or array format |
healthcheck |
Object | Health check configuration | {test: ["CMD", "curl", "localhost"], interval: 30s} |
networks |
Array | Custom networks to join | - frontend, - backend |
container_name |
String | Custom container name | my_app_web |
working_dir |
String | Working directory inside container | /app |
user |
String | Run as specific user | 1000:1000 or "nobody" |
cpu_shares |
Integer | CPU resources (relative weight) | 1024 |
mem_limit |
String | Memory limit | 512m, 1g |
Version Compatibility
Use version 3.9 or higher for most modern use cases:
- 3.9: Latest stable, supports all features
- 3.8: Still widely supported
- 2.1: Legacy, limited features
Healthcheck Configuration
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Essential Docker Compose Commands
| Command | Description | Example |
|---|---|---|
docker compose up |
Start all services defined in compose file | docker compose up or docker compose up -d (detached) |
docker compose down |
Stop and remove containers, networks | docker compose down or with -v to remove volumes |
docker compose ps |
List running services | docker compose ps or docker compose ps -a (all) |
docker compose logs |
View service logs | docker compose logs or docker compose logs -f api (follow) |
docker compose build |
Build or rebuild images | docker compose build or docker compose build --no-cache |
docker compose exec |
Execute command in running container | docker compose exec postgres psql -U postgres |
docker compose run |
Run one-off command in service | docker compose run api npm test |
docker compose stop |
Stop running services (keep containers) | docker compose stop or docker compose stop api |
docker compose start |
Start stopped services | docker compose start or docker compose start postgres |
docker compose restart |
Restart running services | docker compose restart or docker compose restart api |
docker compose pull |
Pull latest images from registry | docker compose pull |
docker compose config |
Validate and display merged compose config | docker compose config |
docker compose kill |
Force stop services | docker compose kill or docker compose kill -s SIGKILL |
docker compose rm |
Remove stopped containers | docker compose rm or docker compose rm -f (force) |
Command Variations & Useful Flags
# Run with specific compose file
docker compose -f docker-compose.prod.yml up -d
# Run in project directory
cd /path/to/project && docker compose up
# Scale specific service
docker compose up --scale api=3
# View logs with timestamps
docker compose logs --timestamps
# Follow logs from specific service
docker compose logs -f postgres
# Rebuild without cache
docker compose build --no-cache api
# Run interactive shell
docker compose exec -it api bash
# Display environment variables in container
docker compose exec api printenv
Common Patterns & Real-World Examples
Pattern 1: Multi-Container Web Application (Nginx + Node.js + PostgreSQL)
version: '3.9'
services:
nginx:
image: nginx:latest
container_name: app_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
- ./html:/usr/share/nginx/html:ro
depends_on:
- api
networks:
- frontend
restart: unless-stopped
api:
build:
context: ./app
dockerfile: Dockerfile
container_name: app_api
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_USER=appuser
- DB_PASSWORD=secure_password
volumes:
- ./app:/app
- /app/node_modules
depends_on:
postgres:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:15-alpine
container_name: app_postgres
environment:
- POSTGRES_USER=appuser
- POSTGRES_PASSWORD=secure_password
- POSTGRES_DB=appdb
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
networks:
frontend:
backend:
Pattern 2: Development vs Production Configuration
docker-compose.yml (base):
version: '3.9'
services:
api:
build: ./app
ports:
- "3000:3000"
environment:
- DB_HOST=postgres
volumes:
- ./app:/app
docker-compose.override.yml (development):
services:
api:
environment:
- NODE_ENV=development
- DEBUG=*
command: npm run dev
docker-compose.prod.yml (production):
services:
api:
environment:
- NODE_ENV=production
- DEBUG=false
command: npm run start
restart: always
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
Usage:
# Development
docker compose up
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Pattern 3: Environment Variable Management with .env
.env file:
# Database
DB_HOST=postgres
DB_USER=appuser
DB_PASSWORD=my_secure_password_here
DB_NAME=production_db
# Application
NODE_ENV=production
APP_PORT=3000
LOG_LEVEL=info
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
docker-compose.yml:
version: '3.9'
services:
api:
image: myapp:latest
environment:
- DB_HOST=${DB_HOST}
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME}
- NODE_ENV=${NODE_ENV}
- APP_PORT=${APP_PORT}
Variables in ${VAR} format are automatically replaced from .env file.
Pattern 4: Networking Between Services
version: '3.9'
services:
frontend:
image: frontend:latest
networks:
- frontend_net
api:
image: api:latest
networks:
- frontend_net
- backend_net
# Frontend can reach API at http://api:3000
database:
image: postgres:15
networks:
- backend_net
# API can reach database at postgres://database:5432
networks:
frontend_net:
driver: bridge
backend_net:
driver: bridge
Services communicate using the service name as hostname. For example, a service named postgres is automatically accessible as postgres:5432 to other services on the same network.
Troubleshooting β 5 Common Issues & Solutions
Issue 1: "ERROR: Service 'api' failed to build"
Dockerfile has syntax errors or missing dependencies
Solution:
# View detailed build output
docker compose build --no-cache api
# Check Dockerfile syntax
docker build --dry-run ./app
Issue 2: "Cannot connect to service: Connection refused"
Services haven't started yet or are on different networks
Solution:
# Verify all services are running
docker compose ps
# Check service logs for startup errors
docker compose logs postgres
# Ensure services are on same network
docker compose exec api ping postgres
# Use depends_on with service_healthy condition
# In docker-compose.yml:
# depends_on:
# postgres:
# condition: service_healthy
Issue 3: "Volume mount permission denied"
Container user doesn't have permissions for mounted directory
Solution:
# Fix permissions on host
chmod 755 /path/to/volume
# Or specify user in docker-compose.yml
user: "1000:1000"
# For database volumes, use proper ownership
chown -R 999:999 ./postgres_data
Issue 4: "Docker Compose hangs or times out"
Services take too long to become ready, or process is stuck
Solution:
# Increase timeout for depends_on
# docker-compose.yml: start_period in healthcheck
# View service logs in real-time
docker compose logs -f
# Force stop (use cautiously)
docker compose kill
# Clear everything and restart
docker compose down -v
docker compose up --build
Issue 5: "Out of disk space" or "Cannot write to volume"
Docker volumes consuming too much disk space
Solution:
# Check disk usage
docker system df
# Remove unused volumes
docker volume prune
# List all volumes
docker volume ls
# Remove specific volume (use with caution!)
docker volume rm volume_name
# Clean everything (development only)
docker compose down -v
docker system prune -a --volumes
β‘ Build Docker Compose Files Visually
No more manual YAML editing! Use our free Docker Compose Generator to create production-ready configurations with drag-and-drop simplicity.
Try the Generator βFrequently Asked Questions
docker-compose (v1): Standalone Python tool, requires separate installation. Slower startup.
Docker Compose (v2): Written in Go, built into Docker, faster startup, uses docker compose instead of docker-compose. Recommended for all new projects.
Usage: docker compose (v2) vs docker-compose (v1)
Use the service-specific restart command:
docker compose restart postgres
Other services remain unaffected. You can also use stop and start separately:
docker compose stop postgres
docker compose start postgres
Yes, but with caveats. Docker Compose is suitable for:
- Small production deployments (single server)
- Staging environments
- Development and testing
For large-scale production, consider:
- Kubernetes: Multi-node orchestration, auto-scaling, advanced networking
- Docker Swarm: Lighter than Kubernetes, built-in to Docker
Use volumes to mount directories on the host into containers:
volumes:
- /host/path:/container/path # Bind mount
- named_volume:/container/path # Named volume
- ./relative/path:/container/path # Relative path
Named volumes are managed by Docker and persist data independently. Bind mounts link directly to host directories. For databases, always use named volumes for data persistence.
docker compose down: Stops and removes containers and networks, but PRESERVES named volumes.
docker compose down -v: Also removes named volumes (β οΈ deletes data!).
Best practice: Always use down without -v unless you specifically want to delete data.
Key Takeaways
- β
Docker Compose simplifies multi-container orchestration with a single
docker-compose.ymlfile - β Use version 3.9+ for all features including healthchecks and dependencies
- β Services communicate via service name as hostname on custom networks
- β
Manage configuration with
.envfiles for environment variables - β
Use
healthcheckanddepends_on: {condition: service_healthy}for reliable startup order - β
Named volumes persist data even after
docker compose down - β For production: single server β, large scale β Kubernetes or Docker Swarm
- Build your first multi-container app with the Docker Compose Generator
- Check out official Docker Compose documentation
- Explore real-world examples: Awesome Compose repository