Objective
Create a Compose stack with a database, an API, and a worker. The API and worker wait for the database to become healthy before starting. Verify the start order is correct.
Tasks
Task 1: Project setup
mkdir -p ~/compose-health-lab && cd ~/compose-health-lab
Task 2: Compose file
# compose.yml
services:
db:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_pw
secrets:
- db_pw
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
api:
image: myorg/api:1.0.0
environment:
DATABASE_URL: postgres://app:$(cat /run/secrets/db_pw)@db:5432/app
secrets:
- db_pw
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 5
worker:
image: myorg/worker:1.0.0
environment:
DATABASE_URL: postgres://app:$(cat /run/secrets/db_pw)@db:5432/app
secrets:
- db_pw
depends_on:
db:
condition: service_healthy
secrets:
db_pw:
file: ./db_pw.txt
volumes:
pgdata:
echo 'supersecret' > db_pw.txt
chmod 0400 db_pw.txt
Task 3: Bring up and observe
docker compose up -d
sleep 15
docker compose ps
The output shows the state for each service. The db should be
healthy; api and worker should be running.
Task 4: Verify ordering with timestamps
docker compose ps --format json | jq -r '.[] | "\(.Service): started \(.State)\n"'
The db StartedAt should precede api and worker StartedAt.
Task 5: Force a failure
Stop the database:
docker compose stop db
docker compose ps
api and worker keep running (they are decoupled at runtime).
But on next docker compose up, they will wait for db to be
healthy before starting.
Task 6: Cleanup
docker compose down --volumes