Docker Compose starts API before Postgres is ready because depends_on only waits for container start

2026-11-13 · 3 min read

Docker Compose starts API before Postgres is ready because depends_on only waits for container start

Your docker compose up starts Postgres and the API together. The API opens a connection to db:5432, gets connection refused, exits, and restarts. Locally that is noisy logs and wasted minutes. In CI it becomes a flaky job that passes on rerun, so nobody fixes it. The short depends_on form is usually the cause: it waits for the container to start, not for Postgres to accept connections. To gate startup on readiness, use docker compose depends_on healthcheck condition.

What depends_on actually guarantees

The short syntax:


depends_on:
  - db

only controls start order. Compose starts the db container before the api container, but “container started” is not “Postgres ready.” Postgres still has to initialize its data directory, start its listener, and accept TCP connections [link needed]. The API can win that race every time on a cold volume.

The long syntax supports conditions: service_started, service_healthy, and service_completed_successfully [link needed]. service_healthy is the one for a database that must accept connections before dependents start.

Use docker compose depends_on healthcheck condition correctly

1. Add a healthcheck to Postgres. Use pg_isready, which exits non-zero until the server accepts connections [link needed].


services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 10s

2. Change the API dependency to the long form and require service_healthy:


  api:
    build: .
    depends_on:
      db:
        condition: service_healthy

3. Keep a small retry loop in the API anyway. A healthcheck is a startup gate, not a replacement for handling transient disconnects after startup.

4. Validate the rendered config before committing:


docker compose config

You should see the long-form depends_on with condition: service_healthy, not a plain list.

service_healthy requires the dependency to define a healthcheck; without one, Compose has nothing to wait for. The start_period gives Postgres time to initialize before failed checks count against retries [link needed].

Verify the gate actually works

Run the database alone and watch its health state:


docker compose up -d db
docker compose ps

Wait until db reports healthy, then start the API:


docker compose up api

The API should start after the healthcheck passes. Check docker compose logs api for connection errors and docker compose events for start order. The docker compose depends_on healthcheck condition setting is working when the API no longer crash-loops on a clean volume.

Also test the failure path: break the healthcheck command, run docker compose up, and confirm the API waits instead of starting. A gate you have never seen block is not verified.

When NOT to do this

Do not add docker compose depends_on healthcheck condition just because it sounds safer. Skip it when:

Also avoid treating pg_isready as proof that migrations ran or that the application schema exists. It checks server availability, not application readiness.

Checklist