DEV Community

Peon Sh
Peon Sh

Posted on Originally published at peon.sh

Postgres “Connection Refused” in Docker: The Complete Fix List

App can’t reach Postgres in Docker? Hostname resolution, networks, startup ordering and auth, the four failure classes and their fixes.

Four failure classes, four different fixes
Every "app cannot reach Postgres in Docker" report is one of four distinct problems: wrong hostname, separate networks, startup ordering, or authentication. The error text tells you which: ECONNREFUSED or "connection refused" is network-level (classes 1 to 3); "password authentication failed" or "no pg_hba.conf entry" means the network is fine and auth is wrong (class 4). Diagnose top-down and you fix it in minutes.

Class 1: localhost is not your database
Inside a container, localhost means that same container, not the machine, not the database next door. An app configured with postgres://user:pass@localhost:5432/db will get ECONNREFUSED forever, because nothing listens on 5432 inside the app’s own container. Use the database’s service or container name as the hostname; Docker’s embedded DNS resolves it on shared user-defined networks:

wrong (inside a container)

DATABASE_URL=postgres://app:secret@localhost:5432/appdb
# right
DATABASE_URL=postgres://app:secret@postgres:5432/appdb
#                                    ^ the service/container name
Enter fullscreen mode Exit fullscreen mode

Class 2: different networks
Containers resolve each other only when they share a user-defined Docker network. Two compose stacks, or a hand-run container and a platform-managed one, land on different networks by default and are mutually invisible. Verify and fix:

Platforms avoid this by attaching all services to a shared network, in Peon, services on the same server reach each other by name out of the box
docker inspect app --format '{{json .NetworkSettings.Networks}}' | jq keys
docker inspect postgres --format '{{json .NetworkSettings.Networks}}' | jq keys
# no common network? connect one:
docker network connect app

Class 3: the startup race
Postgres takes several seconds to initialize, longer on first boot with a fresh volume. An app that connects exactly once at startup loses the race, gets ECONNREFUSED, and crashes into a restart loop that eventually stabilizes (masking the real issue). Fix it properly in both places:

And in the app: retry initial connections with backoff for 30 to 60 seconds, ordering then never matters anywhere (CI, restarts, reboots)

compose: gate on real readiness, not just "started"

depends_on:
postgres:
condition: service_healthy
# postgres service:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 10
Enter fullscreen mode Exit fullscreen mode

Class 4: auth and the first-boot trap
The official image’s POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB variables apply only when initializing an empty data volume. Change them later and nothing happens, the credentials in the existing volume win, a trap that produces "password authentication failed" after an innocent-looking config edit. Fix credentials in the running database (ALTER USER app WITH PASSWORD ‘...’;) or, for throwaway dev data, remove the volume and re-initialize. "No pg_hba.conf entry" appearing with network connections usually means a custom config restricted host access, the stock image already allows network connections from the Docker network with password auth.

Top comments (0)