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
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.