DevOps Tutorial · Containers and Kubernetes · Lesson 18 of 25
Docker Compose
Run an app with its database and cache locally with one Docker Compose file: services, networks, volumes and health checks.
One file for the whole local stack
Docker Compose starts several containers together, with networking between them by service name:
services:
api:
build: .
ports: ["8080:8080"]
environment:
DATABASE_URL: postgresql://app:app@db:5432/app # "db" is the service name
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
volumes: ["pgdata:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
cache:
image: redis:7
volumes:
pgdata:
docker compose up -d --build # build and start everything
docker compose logs -f api
docker compose exec db psql -U app
docker compose down # stop (add -v to delete volumes too)
Where Compose fits
Compose is ideal for local development and CI integration tests: everyone gets the same stack with one command. In production you'd use Kubernetes, ECS, Cloud Run, Azure Container Apps or similar, which add scheduling across machines, self-healing and rolling deployments.
from graphlib import TopologicalSorter
depends_on = {"api": {"db", "cache"}, "worker": {"db", "cache"}, "db": set(), "cache": set()}
order = list(TopologicalSorter(depends_on).static_order())
print("start order:", order)
Output
start order: ['cache', 'db', 'api', 'worker']
Test yourself
In a Compose file, how does the api container reach the database?
By the service name, e.g. db:5432Only via localhost:5432Through the host's public IPIt can't without Kubernetes
Show answer
Answer: By the service name, e.g. db:5432
Compose puts services on a shared network where each service name resolves to its container.