Django "OperationalError: could not connect to server: Connection refused" in CI
Django tried to open a database connection and the TCP connect was refused. In CI this almost always means the Postgres service is not up yet, or the host name and port in DATABASES do not match how the service container is exposed.
What this error means
Migrations or tests fail with "django.db.utils.OperationalError: could not connect to server: Connection refused. Is the server running on host 'localhost' (127.0.0.1) and accepting TCP/IP connections on port 5432?"
django.db.utils.OperationalError: could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 5432?Common causes
Django ran before the Postgres service was ready
The service container is still starting when migrations run. Without a health check, the DB port is not yet accepting connections.
Host or port mismatch with the service
The job connects to localhost but the service is only reachable by its service name, or the mapped port differs from the one in DATABASES.
How to fix it
Add a health check to the Postgres service
Gate the job until pg_isready passes so Django never connects too early.
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
ports: ["5432:5432"]
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s --health-timeout 5s --health-retries 10Point DATABASE_URL at the right host
On GitHub-hosted runners the mapped service is reachable on localhost; align the URL with the exposed port.
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/app_testHow to prevent it
- Always add a pg_isready health check to the Postgres service.
- Match the DATABASES host and port to how the service is exposed.
- Read DATABASE_URL from one place so host and port cannot drift.