Docker Compose: depends_on Doesn't Wait for "Ready" — Healthcheck Ordering

Plain depends_on only orders container START, not readiness — the DB starts before your app and still refuses connections. condition: service_healthy with a healthcheck is the fix; retry loops are the fallback.

What you'll see

Root causes

depends_on without a condition = start-order only

The DB container is RUNNING milliseconds before the app starts — but PostgreSQL/MySQL need seconds to accept connections. The app's first connect hits a listening socket that rejects auth or refuses outright.

No healthcheck defined, so service_healthy has nothing to evaluate

condition: service_healthy requires a healthcheck on the dependency. Without one, compose falls back to started and the race returns silently.

Fix it

  1. Define the dependency's healthcheck
    # db service: healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 10
  2. Gate the app on healthy, not started
    # app service: depends_on: db: condition: service_healthy   # (the compose spec form, not the legacy list form)
  3. App-side resilience regardless (belt and suspenders)
    # connection retry/backoff in the app or wait-for-it.sh — because restarts later in life also need to survive DB blips
  4. Verify the healthcheck passes before trusting it
    docker compose ps   # STATUS shows (healthy) / (health: starting)

Field note

The legacy depends_on: [db] list syntax has no readiness semantics at all — migrating to the mapping form with condition is the actual fix, not a style preference. Healthcheck + healthy-gate fixes cold boot; app retry loops fix everything else (DB restart, failover, long GC pauses). Production systems want both.

Common questions

Why does it work when I restart just the app?

By then the DB is fully up — the race only exists at cold boot when both start together. Classic CI-only failure: cold start every run, while your dev machine keeps a warm DB.

Isn't depends_on supposed to handle this?

Only start ordering — deliberately, because 'ready' is app-specific (a DB may be queryable before being truly ready). The healthcheck is YOU defining ready; compose enforces it.

Ship it right the first time

A production-shaped compose stack: healthchecks, resource limits, log rotation. Never debug a boot race again.

Docker Production Starter — $19 →

One-time. Yours to modify. Instant download from the NinjaOps template store.

Get new fixes by email

One short email when new fixes and production templates drop. No spam, unsubscribe anytime.

Partner pick — sponsored

Vultr — our lab-environment pick for this stack

Spin up a cloud server in 60 seconds and reproduce this fix yourself — pay by the hour.

Get Vultr →
Also vetted

Sentry — Free tier: see the exact line of code that broke, before users report it.

Get Sentry →

We earn a commission if you buy through our links — it never costs you extra. More vetted tools on our picks hub · comparing clouds? DigitalOcean vs Vultr and vs AWS · full deals: DigitalOcean · Vultr · NordLayer · Semrush