Missing "wait for Postgres" Healthcheck - Migration Race in CI
The job starts a database service and immediately runs migrations without waiting for the service to be ready. Sometimes the database is up in time and sometimes it is not, producing flaky "connection refused" failures driven purely by a startup race.
What this error means
The same migrate step passes on some runs and fails with "connection refused" on others, with no code change. The flakiness tracks how quickly the database container happens to boot.
Run npx prisma migrate deploy
Error: P1001: Can't reach database server at `localhost`:`5432`
# (passes on the next run)Common causes
No healthcheck on the service
Without a --health-cmd, GitHub Actions does not wait for the database before starting steps, so the migrate step can win the race.
Fixed sleep instead of readiness probe
A hard-coded sleep 5 is sometimes too short on a slow runner, reintroducing the race.
How to fix it
Add a service healthcheck
Let the job block until the database reports healthy.
services:
db:
image: postgres:16
env: { POSTGRES_PASSWORD: postgres }
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s --health-timeout 5s --health-retries 10Poll readiness instead of sleeping
Replace fixed sleeps with an actual readiness probe.
until pg_isready -h localhost -p 5432; do sleep 1; doneHow to prevent it
- Always pair a database service with a healthcheck or readiness probe.
- Never rely on a fixed sleep to mask a startup race.
- On managed runners (Latchkey), self-healing auto-retries transient failures, and a database that is slow to accept connections is the canonical transient case.