Docker Compose
Run an app with a database and other services from one file.
What you will learn
- Write docker-compose.yml
- Connect services by name
- Persist data with volumes
Real applications are several services: a web app, a database, a cache. Starting each with a long docker run command is tedious and error-prone. Docker Compose describes the whole stack in one YAML file and starts everything with a single command.
services:
api:
build: .
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://app:secret@db:5432/appdb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
retries: 5
cache:
image: redis:7
volumes:
pgdata:docker compose up -d # build if needed and start everything
docker compose ps
docker compose logs -f api
docker compose exec db psql -U app appdb
docker compose down # stop and remove containers
docker compose down -v # ...and delete volumes (data!)Networking by service name
Compose puts all services on a private network where each is reachable by its service name. That is why the URL above uses db and cache as hostnames rather than localhost. Inside a container, localhost means the container itself.
Volumes and data
A named volume (pgdata) keeps the database files across restarts and rebuilds. Without it, your data disappears whenever the container is recreated.
api:
build: .
volumes:
- ./:/app # live-reload: edit code on your machine
command: uvicorn main:app --reload --host 0.0.0.0
env_file:
- .env # keep settings out of the YAMLHealth checks and start order
depends_on alone only waits until the container starts, not until the database is ready to accept connections. Combining it with a healthcheck, as above, makes the app wait for a truly ready database.
Compose is perfect for development and small single-server deployments. For large multi-server production systems teams typically move to an orchestrator such as Kubernetes.
Try it yourself
Write a compose file with a web service using the nginx image on port 8080, and a redis service. Bring it up and check both are running.
Show solution
services:
web:
image: nginx:alpine
ports:
- "8080:80"
redis:
image: redis:7
# docker compose up -d && docker compose ps