Docker Compose
Real applications rarely run in a single container. Docker Compose defines multi-container stacks in a YAML file and manages them with one command.
Why Docker Compose?
A web app typically needs:
- Application server (Node.js, Python)
- Database (PostgreSQL, MySQL)
- Cache (Redis)
- Reverse proxy (Nginx)
Compose lets you define, start, and stop the entire stack:
docker compose up -d # start all services
docker compose down # stop and remove
Basic docker-compose.yml
# docker-compose.yml
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Start the stack:
docker compose up -d
docker compose ps
docker compose logs -f web
Service Configuration
Build vs Image
services:
api:
# Build from Dockerfile in current directory
build:
context: .
dockerfile: Dockerfile
args:
BUILD_VERSION: "1.0.0"
nginx:
# Use pre-built image from registry
image: nginx:1.25-alpine
Environment Variables
services:
web:
environment:
NODE_ENV: production
PORT: 3000
env_file:
- .env
- .env.production
.env file (not committed to Git):
DATABASE_URL=postgres://user:pass@db:5432/myapp
JWT_SECRET=your-secret-here
Port Mapping
ports:
- "8080:80" # host:container
- "127.0.0.1:3000:3000" # bind to localhost only
Volumes
Persist data beyond container lifecycle:
volumes:
# Named volume (managed by Docker)
pgdata:
services:
db:
volumes:
- pgdata:/var/lib/postgresql/data
web:
# Bind mount — sync local files for development
volumes:
- ./src:/app/src:ro # read-only mount
Networks
Services on the same Compose network communicate by service name:
networks:
frontend:
backend:
services:
web:
networks:
- frontend
- backend
db:
networks:
- backend # not exposed to frontend network
The web service reaches the database at hostname db (the service name).
Development vs Production Overrides
# docker-compose.override.yml (auto-loaded in dev)
services:
web:
build:
target: development
volumes:
- ./src:/app/src
command: npm run dev
environment:
NODE_ENV: development
# docker-compose.prod.yml
services:
web:
restart: always
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
Production:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Full Stack Example — Laravel + MySQL + Redis
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/var/www/html
depends_on:
- db
- redis
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- .:/var/www/html
depends_on:
- app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
volumes:
- mysqldata:/var/lib/mysql
redis:
image: redis:7-alpine
volumes:
mysqldata:
Common Commands
docker compose up -d # Start detached
docker compose up --build # Rebuild images first
docker compose down # Stop and remove containers
docker compose down -v # Also remove volumes (DATA LOSS)
docker compose ps # List services
docker compose logs web # Logs for one service
docker compose logs -f --tail=50 # Follow last 50 lines
docker compose exec web sh # Shell into running container
docker compose exec db psql -U user -d myapp
docker compose restart web # Restart one service
docker compose pull # Pull latest images
depends_on and Healthchecks
depends_on alone only waits for container start — not readiness. Use healthchecks:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
retries: 5
web:
depends_on:
db:
condition: service_healthy
Scaling Services
docker compose up -d --scale web=3
Requires a load balancer (Nginx, Traefik) in front — Compose alone does not load-balance.
What Comes Next
Learn production security, image scanning, and orchestration alternatives in Production Docker, then automate builds with CI/CD.