Docker Compose describes a multi-container application in one YAML file and runs it with a single command. Ideal for local development, testing and small single-host deployments. Base concepts are in 1. Docker.
Why Compose
- One file defines services, networks and volumes
docker compose upreplaces many longdocker runcommands- Reproducible dev environments that live in Git
- Compose creates a default network where services reach each other by service name
Compose is the V2 plugin: use docker compose (with a space), not the legacy docker-compose.
Example: Web + Database + Cache
# compose.yaml
services:
web:
build: . # build from ./Dockerfile
ports:
- "8000:8000"
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/appdb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
volumes:
- ./src:/app/src # bind mount for live code reload
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 10
cache:
image: redis:7-alpine
volumes:
pgdata:
Notes:
- web reaches Postgres at host db, Redis at cache (service names are DNS names)
- depends_on with condition: service_healthy waits for the healthcheck, plain depends_on only orders start, not readiness
- Only web publishes a port; db and cache stay private to the Compose network
- ${DB_PASSWORD} is read from the shell or a .env file next to compose.yaml (keep .env out of Git)
Commands
$ docker compose up -d # create and start in background
$ docker compose up --build # rebuild images first
$ docker compose ps # status of services
$ docker compose logs -f web # follow logs for one service
$ docker compose exec web sh # shell in a running service
$ docker compose run --rm web pytest # one-off container for a task
$ docker compose stop # stop, keep containers
$ docker compose down # stop and remove containers + network
$ docker compose down -v # ...and remove named volumes (deletes data!)
$ docker compose config # validate and print resolved config
$ docker compose pull # pull newer images
Service Options
| Key | Purpose |
|---|---|
image / build |
Use an image, or build from a context + Dockerfile |
ports |
"host:container" publish |
expose |
Container-only port (documentation / inter-service) |
environment / env_file |
Environment variables |
volumes |
Named volumes and bind mounts |
networks |
Attach to specific networks |
depends_on |
Start order, optionally wait for health |
healthcheck |
Define readiness |
restart |
no, always, on-failure, unless-stopped |
command / entrypoint |
Override image defaults |
deploy.resources.limits |
CPU / memory limits |
profiles |
Optional services enabled on demand |
secrets / configs |
Mount sensitive or config files |
Build options
services:
web:
build:
context: .
dockerfile: Dockerfile
target: dev # multi-stage target
args:
VERSION: "1.0"
Networks
services:
web:
networks: [frontend, backend]
db:
networks: [backend] # not reachable from frontend-only services
networks:
frontend:
backend:
Profiles (optional services)
services:
adminer:
image: adminer
profiles: ["debug"]
ports: ["8080:8080"]
$ docker compose --profile debug up -d
Configuration Management
Environment variable precedence (highest first)
docker compose run -e- Shell environment
--env-file.envfileenvironment:/env_file:in the Compose fileENVin the image
Multiple files for environments
Base file plus overrides, merged in order.
compose.yaml # shared
compose.override.yaml # auto-loaded in dev (bind mounts, debug)
compose.prod.yaml # production tweaks
$ docker compose -f compose.yaml -f compose.prod.yaml up -d
Secrets
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Development Workflow
- Bind-mount source code for hot reload, named volumes for databases and
node_modules docker compose watch(withdevelop.watch) syncs or rebuilds on file changes- Use a healthcheck on every dependency
- Seed data by mounting scripts into
/docker-entrypoint-initdb.d(Postgres / MySQL images) - Run tests in CI:
docker compose up -d --wait, test, thendocker compose down -v(see CI-CD Pipelines)
Compose vs Kubernetes
| Compose | Kubernetes | |
|---|---|---|
| Scope | One host | Cluster of nodes |
| Scaling | --scale web=3 manually |
Autoscaling, self-healing |
| Rollouts | Recreate | Rolling updates, rollbacks |
| Complexity | Low | High |
| Best for | Dev, CI, small apps | Production at scale |
A Compose file can be translated into Kubernetes manifests with tools like Kompose. See 4. Running Your Containers in Kubernetes.