Skip to content

Chapter 2 of 8

Services, Dependencies, and Health

The depends_on: key controls the order in which Compose starts services, but it has a sharp edge that catches almost everyone the first time. The short form depends_on: [db, redis] and the long form with condition: service_started only wait for the container to be created and started, not for the service inside that container to be ready to accept connections. For a database, that means your app may begin trying to connect before the database has finished initializing. The fix is to add a healthcheck: to the dependency and use condition: service_healthy instead.

A healthcheck is a per-service test that Docker runs inside the container on a schedule. For Postgres, a typical healthcheck uses pg_isready -U postgres; for a web service you might curl a health endpoint. The schema accepts test (the command to run), interval (how often to run it), timeout, retries (consecutive failures before marking unhealthy), and start_period (an initial grace window during which failures don't count). The defaults are 30s interval, 30s timeout, 3 retries, and no start period, which is too aggressive for slow-starting databases, so tune these for your workload. Once a service has a healthcheck, dependents can wait on it via depends_on: db: condition: service_healthy, and even better, the dependent itself should retry its connection on startup rather than assuming a single attempt is enough.

Beyond service_healthy, Compose offers service_completed_successfully for one-shot init or migration containers. You mark a service as a one-off with restart: 'no', give it a command that exits when its work is done, and then other services depend on it with the completed-successfully condition. This is the cleanest pattern for running database migrations or seed scripts before the main app boots. Restart policies are also part of service health: restart: always restarts no matter how the container exited, on-failure[:N] only restarts on a non-zero exit with an optional retry cap, and unless-stopped restarts always except when you explicitly stopped it. For local development, unless-stopped is usually the right default because it survives reboots but lets you take a service down deliberately.

All chapters
  1. 1Compose File Anatomy
  2. 2Services, Dependencies, and Health
  3. 3Volumes, Mounts, and Data Persistence
  4. 4Networking in Compose
  5. 5Building, Scaling, and Resources
  6. 6Multi-Environment Compose and Configuration
  7. 7Logs, Debugging, and Common Gotchas
  8. 8Stack-Specific Recipes and Patterns

Drill it

Reading is not remembering. These come from the Docker Compose Recipes For Local Dev deck:

Q

Compose file directive to mount a host directory into a container?

volumes: - ./local:/app

Q

Restart a container automatically unless explicitly stopped?

restart: unless-stopped

Q

Define an environment variable in Compose?

environment:<br> - KEY=value or env_file: .env

Q

Wait for another service to be 'healthy' before starting?

depends_on:<br> db:<br> condition: service_healthy