DEV Community

Cover image for docker compose depends_on does not wait for your database: fix "connection refused" on startup with healthchecks
TANK JAY
TANK JAY

Posted on Originally published at jaytank.hashnode.dev

docker compose depends_on does not wait for your database: fix "connection refused" on startup with healthchecks

api-1  | Error: connect ECONNREFUSED 172.18.0.2:5432
api-1 exited with code 1
db-1   | LOG:  database system is ready to accept connections
Enter fullscreen mode Exit fullscreen mode

The app died one line before Postgres was ready. Run docker compose up again and it works. Your compose file says depends_on: [db], so what happened?

depends_on waits for "running", not "ready"

The list form of depends_on means condition: service_started. Compose starts db, sees the container running, starts your app. Meanwhile Postgres is still running initdb and your init scripts. The Docker docs say it directly: Compose waits for running, not ready.

The trap: service_healthy with no healthcheck

So you switch to condition: service_healthy. But the official postgres, mysql, mongo, redis and rabbitmq images define no HEALTHCHECK (on purpose). Without a healthcheck: in compose, startup fails with "has no healthcheck configured" or "container is unhealthy" instead of waiting.

The fix

services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $${POSTGRES_USER:-postgres}"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 30s
Enter fullscreen mode Exit fullscreen mode

$$ stops Compose interpolating the variable on your host. start_period keeps a slow first boot from burning your retries. And keep a connection retry in the app anyway: databases restart in production too.

Flag it in the editor

I built ReadyWait, a free VS Code extension that checks every compose file in the workspace:

  • RW001 short-form depends_on / service_started on postgres, mysql, redis, kafka, rabbitmq and other slow services
  • RW002 service_healthy on a service with no (or a disabled) healthcheck
  • RW003 no start_period, or timeout longer than interval
  • RW004 service_completed_successfully on a restart: always service

One-click fixes convert depends_on to long form and insert pg_isready, mysqladmin ping, redis-cli ping, rabbitmq-diagnostics and similar probes where the tool ships in the official image. Override files and local Dockerfile HEALTHCHECKs count. Fully static: no network, no telemetry, no docker calls. It cannot see a HEALTHCHECK baked into a registry image, and it does not follow YAML anchors or include:.

code --install-extension jaytankdev.readywait
Enter fullscreen mode Exit fullscreen mode

Source (MIT): github.com/jay-tank/readywait

Full write-up (why startup races happen, every healthcheck command, limitations, FAQ): docker compose depends_on does not wait for your database

Top comments (1)

Collapse
 
abhinavs1920 profile image
Abhinav Sharma •

The official images shipping no HEALTHCHECK on purpose is what makes this a trap instead of a missing feature: the correct fix fails in a new way ("has no healthcheck configured") and sends people back to the list form. Did you consider retrying app-side instead? The compose healthcheck proves postgres accepts connections, not that your migrations ran.