Postgres connection refused (tested)

connection refused and could not connect to server mean your app reached out for Postgres and found nobody there. We ran a real Postgres in Docker, then killed it, mistyped its password and pointed at a hostname that does not resolve, to capture the exact error for each case.

1. The error you are probably seeing

In Node, the pg driver throws an Error whose code is ECONNREFUSED when a TCP connection to the given host and port is actively rejected. We measured this directly: connecting to port 5434 (nothing listening) with pg 8.23.1 produced this error 3 ms after the connect call:

message: connect ECONNREFUSED 127.0.0.1:5434
code: ECONNREFUSED
errno: -111
syscall: connect
address: 127.0.0.1
port: 5434

The same error, same shape, is what psql's could not connect to server: Connection refused message means per the PostgreSQL docs (not measured here, since this box has no psql binary): the OS rejected the TCP connection outright, almost always because nothing is listening on that host/port.

2. Our test setup

This box has Docker but no Postgres client binaries (which psql pg_isready returned nothing). So we ran a real, throwaway Postgres server and drove it with Node's pg driver instead of psql.

docker run --rm -d --name forever-pgtest -p 5433:5432 \
  -e POSTGRES_PASSWORD=test postgres:16-alpine

It logged database system is ready to accept connections 3 seconds after start. We then connected with the correct password, with a wrong password, with a hostname that cannot resolve, and finally stopped the container and reconnected to the same port to capture the exact moment "listening" becomes "refused".

3. Results: four different errors, four different fixes

ScenarioWhat we didExact errorcodeMeaning
Nothing listening on the portConnected to closed port 5434connect ECONNREFUSED 127.0.0.1:5434ECONNREFUSEDPostgres is not running, or not on this port
Server up, correct passwordConnected to live postgres:16-alpine on 5433connected in 26 ms, PostgreSQL 16.15 returned–Happy path, included for comparison
Server up, wrong passwordSame server, wrong passwordpassword authentication failed for user "postgres"28P01Server reachable, credentials wrong — not a network problem
Hostname does not resolveConnected to host db (a Docker Compose-style service name), no DNS record for itgetaddrinfo ENOTFOUND dbENOTFOUNDApp is not on the same Docker network as the one defining that hostname
Server stopped mid-sessiondocker stop forever-pgtest, then reconnected to 5433connect ECONNREFUSED 127.0.0.1:5433ECONNREFUSEDSame shape as case 1 — the driver cannot tell "never started" from "just stopped"

The important distinction: ECONNREFUSED and 28P01 (password authentication failed) are different errors that look similar in a panicked read of the logs but mean opposite things. ECONNREFUSED means nothing answered the TCP connection at all — the fix is on the network/process side. 28P01 means Postgres answered and rejected your credentials — the fix is in your connection string or pg_hba.conf, and the network is fine.

4. The classic Docker Compose race

If your app and Postgres are both Docker Compose services, a very common sequence is: Compose starts both containers at once, the app container's process reaches its connect() call in milliseconds, but Postgres takes a few seconds to initialize (we measured 3 seconds for postgres:16-alpine to log "ready to accept connections" from a cold image pull plus init). During that window the app gets exactly the ECONNREFUSED we measured in case 1, because the TCP listener genuinely does not exist yet.

This is distinct from a wrong-password failure (case 3), which only happens once Postgres is actually listening. If you see ECONNREFUSED immediately on every container start but it goes away on a manual retry seconds later, that is the startup race, not a configuration bug. Per Docker Compose docs (not measured here), depends_on: condition: service_healthy with a pg_isready healthcheck is the standard fix, since depends_on alone only waits for the container to start, not for Postgres inside it to be ready.

5. Why a hostname/network mismatch produces a different error

We measured that connecting to a hostname with no DNS entry (db, a typical Compose service name) gives ENOTFOUND, not ECONNREFUSED. That distinction matters for diagnosis:

If you're debugging across a Docker bridge network vs. host networking, per Docker docs (not measured), containers on the default bridge network reach each other by service name and the container's internal port, not by localhost/127.0.0.1 — localhost inside a container refers to that container itself. Using localhost for the Postgres host from inside another container is a common cause of ECONNREFUSED that looks identical to "Postgres isn't running" but is actually a networking-scope mistake.

6. Common causes, ranked

7. How to fix it, in order

If the DNS name itself is the problem rather than the port, see DNS lookup. For the general Node.js version of this error outside a database context, see ERR_CONNECTION_REFUSED / ECONNREFUSED. If your client gives up before any of this resolves, see 408 Request Timeout.

Frequently asked questions

What is the exact error code for a Postgres connection refused in Node?

With the pg npm driver (measured, v8.23.1) the thrown error has code ECONNREFUSED and a message like "connect ECONNREFUSED 127.0.0.1:5433". We reproduced this against both a closed port and a Postgres container we stopped mid-test.

Is "connection refused" the same as "password authentication failed"?

No. We measured both: ECONNREFUSED means nothing answered the TCP connection at all. "password authentication failed for user..." (code 28P01) means Postgres answered and rejected your credentials. The first is a network/process problem; the second is a config/credentials problem.

Why does my app get connection refused only when started with Docker Compose?

Most likely the startup race: Compose starts the app and Postgres containers together, but Postgres takes time to initialize. We measured 3 seconds for a fresh postgres:16-alpine container to log ready. An app that connects immediately with no retry hits ECONNREFUSED in that window. Per Docker docs, a healthcheck plus depends_on: condition: service_healthy fixes this.

I get ENOTFOUND instead of ECONNREFUSED — what does that mean?

We measured ENOTFOUND (getaddrinfo ENOTFOUND) when connecting to a hostname that has no DNS record at all, such as a Docker Compose service name used from a container not on that Compose network. ECONNREFUSED means the name resolved but nothing was listening; ENOTFOUND means the name never resolved.

Could Postgres be running but still refuse connections?

Yes, per the PostgreSQL docs (not measured on this box): if listen_addresses is set to only 127.0.0.1, Postgres refuses connections arriving from any other network interface even while the process runs and logs normally. We did measure that a running, reachable Postgres with the wrong password gives a completely different error (28P01), confirming reachability and auth are checked separately.

Written with AI assistance. Measured on 2026-10-02 with Node.js 24.21.0 and the `pg` npm driver v8.23.1, against a real postgres:16-alpine container (PostgreSQL 16.15) run via Docker on 127.0.0.1:5433, and against a definitely-closed local port 5434. This box has no psql or pg_isready binaries, only Docker. Claims about nginx, multi-OS socket paths or application-level retry logic are labelled as such and were not measured. PlainHeaders is our extension; Node.js, the pg driver and Docker are free.