You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Real applications consist of multiple services — a web server, an API, a database, a cache, a message queue. Managing these with individual docker run commands is tedious and error-prone. Docker Compose lets you define and manage multi-container applications in a single YAML file.
Docker Compose is a tool for defining and running multi-container Docker applications. You describe your entire application stack in a docker-compose.yml (or compose.yaml) file, then use a single command to create and start everything.
# Start all services defined in docker-compose.yml
docker compose up -d
# Stop all services
docker compose down
# docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- api
api:
build: ./api
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
| Key | Purpose |
|---|---|
services | Define the containers that make up your app |
networks | Define custom networks |
volumes | Define named volumes |
configs | Define configuration objects (Swarm) |
secrets | Define secrets (Swarm) |
services:
api:
build:
context: ./api
dockerfile: Dockerfile.prod
args:
NODE_ENV: production
image: my-app:latest # Also tag the built image
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
services:
api:
image: my-api:latest
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_PORT=5432
# Or load from a file
env_file:
- .env
services:
web:
image: nginx:alpine
ports:
- "8080:80" # HOST:CONTAINER
- "8443:443"
- "127.0.0.1:9090:9090" # Bind to specific interface
Docker Compose automatically creates a default network for your project. All services can reach each other by their service name.
# The API can connect to "db:5432" — no manual network configuration needed
services:
api:
image: my-api:latest
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
db:
image: postgres:16
services:
web:
image: nginx:alpine
networks:
- frontend
api:
image: my-api:latest
networks:
- frontend
- backend
db:
image: postgres:16
networks:
- backend
networks:
frontend:
backend:
services:
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data # Named volume
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # Bind mount
- /tmp/cache:/cache:ro # Read-only bind mount
volumes:
db-data:
driver: local
depends_on controls startup order, but it does not wait for a service to be "ready" — only that the container has started.
services:
api:
depends_on:
- db
- redis
db:
image: postgres:16
redis:
image: redis:7-alpine
For true readiness checks, use depends_on with condition:
services:
api:
build: ./api
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
Health checks tell Docker how to determine if a container is healthy:
services:
api:
build: ./api
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.