Skip to Content
Wait for a Dependency With a Healthcheck
Intermediate
0 likes
Tutorials

Wait for a Dependency With a Healthcheck

Add a pg_isready health check to a database and make the app wait for it with service_healthy. After this, you can stop an app from failing on a database that is still starting.

Your app starts, tries to reach the database, and fails because PostgreSQL needs a few more seconds. Compose started the database first, as you asked. It just did not wait until the database could answer. A health check on the database, plus one line on the app, fixes that. This page shows the failure and the fix with a real PostgreSQL container.

The failure

The app service here is only a psql client that connects once and prints a line. The password is made up.

services:
  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_PASSWORD: not-a-real-password
  app:
    image: postgres:18-alpine
    environment:
      PGPASSWORD: not-a-real-password
    command: psql -h db -U postgres -c "select 'connected' as status"
    depends_on:
      - db
Set-Location dbwait-stack
docker compose up -d 2>&1 | Out-Null
Start-Sleep -Seconds 4
docker compose logs --no-log-prefix app
psql: error: connection to server at "db" (172.28.0.2), port 5432 failed: Connection refused
	Is the server running on that host and accepting TCP/IP connections?

The error is the "Connection refused" line. depends_on waited for the db container to start, which takes a moment, and not for PostgreSQL inside it, which takes several seconds the first time. The app was one step ahead.

docker compose down -v

The fix: a health check and a condition

PostgreSQL comes with a tool for exactly this, pg_isready. It exits with 0 when the server accepts connections. Add it as the health check and make the app wait for service_healthy.

services:
  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_PASSWORD: not-a-real-password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
      interval: 2s
      timeout: 3s
      retries: 10
      start_period: 5s
  app:
    image: postgres:18-alpine
    environment:
      PGPASSWORD: not-a-real-password
    command: psql -h db -U postgres -c "select 'connected' as status"
    depends_on:
      db:
        condition: service_healthy
docker compose -f compose.fixed.yaml up -d 2>&1 | Out-Null
Start-Sleep -Seconds 2
docker compose -f compose.fixed.yaml logs --no-log-prefix app
docker inspect --format "{{.State.Health.Status}}" dbwait-stack-db-1
  status
-----------
 connected
(1 row)

healthy

Compose held the app back until pg_isready passed, and then the connection worked. The health check settings in short: interval is how often it runs, retries is how many failures in a row count as unhealthy, and start_period gives a slow first start some room, so early failures do not count.

💡 Tip: Check readiness with a tool that speaks the service's own protocol, such as pg_isready for PostgreSQL. Checking that a port is open is weaker, because a server can accept connections before it is able to serve queries.

âš  Warning: The health check only helps at start. If the database goes away later, the app still needs its own retry logic. depends_on is a start-up order, not a promise that the database stays up.

docker compose -f compose.fixed.yaml down -v

Related course: Docker Intermediate.

About

Add a pg_isready health check to a database and make the app wait for it with service_healthy. After this, you can stop an app from failing on a database that is still starting.

Responsible
John G
Last Update
08/13/2026
Reading Time
3 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.