Docker Compose
Learn how to use Docker Compose to manage multi-container applications with ease.
What is 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 start everything with a single command.
Why Use Docker Compose?
Docker Compose is perfect for:
- Development environments with multiple services
- Applications that need databases, caches, and other dependencies
- Testing complex application stacks
- Simplifying container orchestration for small to medium applications
Basic Docker Compose File
Here’s a simple docker-compose.yml for a web application with a database:
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- api
api:
build: ./api
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=database
depends_on:
- database
database:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:Key Docker Compose Concepts
Services
Services define the containers that make up your application. Each service runs one container image.
services:
web:
image: nginx:alpine
redis:
image: redis:alpineImages
You can use existing images or build from a Dockerfile:
services:
# Use existing image
web:
image: nginx:alpine
# Build from Dockerfile
api:
build: ./api
# or with build context and Dockerfile path
build:
context: ./api
dockerfile: Dockerfile.prodPorts
Map ports between the host and containers:
services:
web:
image: nginx
ports:
- "8080:80" # host:container
- "80:80" # same port
- "443" # random host port to container port 443Environment Variables
Set environment variables for your services:
services:
api:
image: node:18
environment:
- NODE_ENV=production
- API_KEY=secret123
- DB_HOST=database
# or using a map
environment:
NODE_ENV: production
API_KEY: secret123Volumes
Persist data and share files between host and containers:
services:
database:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data # named volume
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # bind mount
volumes:
postgres_data: # named volume definitionNetworks
Create custom networks for service communication:
services:
web:
image: nginx
networks:
- frontend
api:
image: node
networks:
- frontend
- backend
networks:
frontend:
backend:Dependencies
Control service startup order:
services:
web:
image: nginx
depends_on:
- api
api:
image: node
depends_on:
- database
database:
image: postgresCommon Docker Compose Commands
Start Services
# Start all services in detached mode
docker-compose up -d
# Start specific service
docker-compose up web
# Start with rebuild
docker-compose up --buildStop Services
# Stop and remove containers
docker-compose down
# Stop but keep containers
docker-compose stop
# Stop and remove everything (volumes, networks)
docker-compose down --volumesView Status
# List running services
docker-compose ps
# View service logs
docker-compose logs
# Follow logs for specific service
docker-compose logs -f web
# Show logs from last 10 minutes
docker-compose logs --since=10mExecute Commands
# Run command in service
docker-compose exec web ls /usr/share/nginx/html
# Get shell in service
docker-compose exec api sh
# Run one-off command
docker-compose run --rm web nginx -tReal-World Examples
Full-Stack Application
version: '3.8'
services:
# Frontend (React)
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:5000
volumes:
- ./frontend:/app
- /app/node_modules
depends_on:
- backend
# Backend (Node.js)
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@database:5432/myapp
- REDIS_URL=redis://redis:6379
volumes:
- ./backend:/app
- /app/node_modules
depends_on:
- database
- redis
# Database (PostgreSQL)
database:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
# Cache (Redis)
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
# Reverse Proxy (Nginx)
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- frontend
- backend
volumes:
postgres_data:
redis_data:Development vs Production
Use multiple Compose files for different environments:
docker-compose.yml (base):
version: '3.8'
services:
web:
build: .
environment:
- NODE_ENV=development
volumes:
- .:/app
- /app/node_modulesdocker-compose.prod.yml (production overrides):
version: '3.8'
services:
web:
environment:
- NODE_ENV=production
volumes: # Remove development volumesRun with production overrides:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml upEnvironment Variables
Use .env file for configuration:
# .env
NODE_ENV=development
DB_HOST=localhost
DB_PORT=5432Reference in docker-compose.yml:
services:
api:
image: node:18
environment:
- NODE_ENV=${NODE_ENV:-production}
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}Health Checks
Add health checks to your services:
services:
api:
image: node:18
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s