PostgreSQL Error Guide: 'Connection refused' Cannot Connect to Server
Fix PostgreSQL 'could not connect to server: Connection refused': diagnose a stopped server, listen_addresses binding, wrong port, firewalls, localhost-only sockets, and SELinux.
- #postgres
- #troubleshooting
- #errors
- #connectivity
Overview
PostgreSQL returns “Connection refused” when the client’s TCP connection reaches the target host but no process is listening on the requested address and port. The kernel actively rejects the connection with an RST. This is a transport-layer failure that happens before any PostgreSQL authentication — the server either is not running, is not listening on the interface you targeted, or something between the two is bound to the wrong place.
You will see this in the client output:
psql: error: connection to server at "db.internal" (10.0.4.10), port 5432 failed:
could not connect to server: Connection refused
Is the server running on host "db.internal" (10.0.4.10) and accepting
TCP/IP connections on port 5432?
It occurs the instant the client tries to open the socket. Crucially, “Connection refused” means the host replied with a refusal — distinct from a timeout (firewall dropping packets) or “no such host” (DNS). A refusal almost always means “right host, nothing listening there.”
Symptoms
- Client fails immediately (not after a timeout) with
could not connect ... Connection refused. localhost/socket connections may work while TCP connections from other hosts fail, or vice versa.pg_isreadyreports the host is not accepting connections.- Nothing is listening on the expected port when you check with
ss/netstat.
pg_isready -h 10.0.4.10 -p 5432
10.0.4.10:5432 - no response
# On the database host
sudo ss -ltnp | grep 5432
(no output)
No listener on 5432 means the refusal is local to the server: either Postgres is down or it is bound elsewhere.
Common Root Causes
1. PostgreSQL is not running
If the service is stopped or crashed, nothing binds the port and every connection is refused.
sudo systemctl status postgresql
● postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled)
Active: failed (Result: exit-code) since Tue 2026-06-23 13:40:02 UTC
Process: 4112 ExecStart=/usr/bin/pg_ctl start ... (code=exited, status=1/FAILURE)
Active: failed (or inactive (dead)) confirms the server is down. Check the log for why before restarting blindly.
2. listen_addresses not set for the interface
By default PostgreSQL listens only on localhost. If listen_addresses does not include the external interface (or *), remote TCP connections are refused even though the server is up.
sudo -u postgres psql -tAc "SHOW listen_addresses;"
localhost
localhost means the server accepts only loopback connections; clients from other hosts get refused. Set listen_addresses = '*' (or the specific IP) and restart.
3. Connecting to the wrong port
A cluster running on a non-default port (or a second cluster on 5433) refuses connections on 5432 because nothing is listening there.
sudo ss -ltnp | grep -i postgres
LISTEN 0 200 0.0.0.0:5433 0.0.0.0:* users:(("postgres",pid=2210,fd=7))
Postgres is listening on 5433, not 5432 — the client targeting 5432 is refused. Confirm with SHOW port; and point the client at the right port.
4. A firewall is blocking the port
A host or network firewall can reject the connection on the port. (A drop rule causes a timeout; a reject rule produces an immediate “Connection refused”.)
sudo firewall-cmd --list-ports
sudo iptables -L INPUT -n --line-numbers | grep 5432
22/tcp 80/tcp 443/tcp
2 REJECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5432 reject-with icmp-port-unreachable
Port 5432 is missing from the allowed list and an explicit REJECT rule sends the refusal. Open the port: sudo firewall-cmd --add-port=5432/tcp --permanent && sudo firewall-cmd --reload.
5. Server bound to localhost only (Unix socket vs TCP)
The server may be running and answering on the Unix socket (so local psql works) while not listening on any TCP address, so every networked client is refused.
# Works locally over the socket
sudo -u postgres psql -h /var/run/postgresql -c 'select 1' >/dev/null && echo "socket OK"
# But no TCP listener
sudo ss -ltn | grep ':5432' || echo "no TCP listener on 5432"
socket OK
no TCP listener on 5432
Socket connections succeed but there is no TCP listener — a classic listen_addresses gap that only bites remote clients.
6. postgresql.conf port mismatch (config vs running)
An edited but unreloaded/unrestarted postgresql.conf, or two clusters reading different config, can leave the running port different from what you expect.
grep -E "^[#]?port" /etc/postgresql/16/main/postgresql.conf
sudo -u postgres psql -tAc "SHOW port;"
port = 5432
5433
The config file says 5432 but the live server reports 5433 — the change was never applied with a restart, so clients hit the old/wrong port and are refused.
Diagnostic Workflow
Step 1: Distinguish refusal from timeout and DNS
pg_isready -h <HOST> -p <PORT>
getent hosts <HOST>
pg_isready returning “no response” with an immediate failure is a refusal (something rejected you), not a hang. Confirm DNS resolves so you know you are hitting the intended IP.
Step 2: Confirm the server is running on the database host
sudo systemctl status postgresql
sudo -u postgres pg_lsclusters 2>/dev/null # Debian/Ubuntu
If the service is failed/dead, that is the answer. Read the log before restarting:
sudo journalctl -u postgresql --no-pager | tail -30
Step 3: Check what is actually listening, and where
sudo ss -ltnp | grep -E 'postgres|:543'
Compare the bound address and port to what the client targets. No listener at all means the server is down or bound only to the socket; a listener on a different port means a port mismatch.
Step 4: Verify listen_addresses and port from the live server
sudo -u postgres psql -tAc "SHOW listen_addresses;"
sudo -u postgres psql -tAc "SHOW port;"
localhost-only listen_addresses refuses remote clients; a port that differs from the client’s target explains the refusal. Fix in postgresql.conf and restart (these settings require a restart, not just a reload).
Step 5: Rule out the firewall and SELinux
sudo firewall-cmd --list-ports
sudo iptables -L INPUT -n | grep 5432
sudo getenforce
sudo grep -i 'name_connect\|postgresql' /var/log/audit/audit.log | tail -5
A REJECT rule produces the refusal; SELinux in Enforcing mode can block PostgreSQL from binding a non-standard port (semanage port -a -t postgresql_port_t -p tcp 5433 allows it). Check audit.log for AVC denials when the listener is missing despite a running server.
Example Root Cause Analysis
An application tier on 10.0.4.30 cannot reach the database at db.internal:5432 and logs could not connect to server: Connection refused. Local psql on the DB host works fine, which rules out a crashed server.
pg_isready confirms an immediate refusal, not a timeout:
pg_isready -h db.internal -p 5432
db.internal:5432 - no response
On the database host, the server is up but the listener is loopback-only:
sudo ss -ltnp | grep 5432
LISTEN 0 200 127.0.0.1:5432 127.0.0.1:* users:(("postgres",pid=1980,fd=6))
LISTEN 0 200 ::1:5432 ::1:* users:(("postgres",pid=1980,fd=7))
PostgreSQL is bound only to 127.0.0.1 and ::1. Local connections (socket and loopback) succeed, which is why on-host psql worked, but the app host on 10.0.4.30 reaches the interface and finds nothing listening there — an immediate refusal. Confirming the setting:
sudo -u postgres psql -tAc "SHOW listen_addresses;"
localhost
Fix: bind the external interface and restart so the change takes effect:
# In postgresql.conf: listen_addresses = '*' (or = '10.0.4.10')
sudo systemctl restart postgresql
sudo ss -ltn | grep 5432
LISTEN 0 200 0.0.0.0:5432 0.0.0.0:*
With the server now listening on 0.0.0.0:5432 (and pg_hba.conf already allowing the app subnet), the application connects successfully.
Prevention Best Practices
- Set
listen_addressesexplicitly to the interfaces clients use during provisioning, and remember it requires a restart — not justpg_reload_conf()— to take effect. - Pin and document the port in
postgresql.conf, and verifySHOW port;matches after any config change so the running server never drifts from expectations. - Use
rejectfirewall rules only deliberately; for database ports, prefer allow-listing the application subnets and alerting when the rule set changes. - Run
pg_isreadyfrom the application host (not just the DB host) in health checks so a localhost-only binding is caught before users are. - When running non-default ports under SELinux, register them with
semanage portso the server can actually bind, and watchaudit.logfor AVC denials. - For fast triage, the free incident assistant can turn a
ss -ltnplisting plus the connection error into the likely binding/port/firewall cause.
Quick Command Reference
# Refusal vs timeout vs DNS
pg_isready -h <HOST> -p <PORT>
getent hosts <HOST>
# Is the server running? Why did it stop?
sudo systemctl status postgresql
sudo journalctl -u postgresql --no-pager | tail -30
# What is listening, and where?
sudo ss -ltnp | grep -E 'postgres|:543'
# Live server's bind address and port
sudo -u postgres psql -tAc "SHOW listen_addresses;"
sudo -u postgres psql -tAc "SHOW port;"
# Firewall and SELinux
sudo firewall-cmd --list-ports
sudo iptables -L INPUT -n | grep 5432
sudo getenforce
sudo grep -i 'postgresql' /var/log/audit/audit.log | tail -5
# Apply listen_addresses/port changes (restart required)
sudo systemctl restart postgresql
# Open the port / allow a non-default SELinux port
sudo firewall-cmd --add-port=5432/tcp --permanent && sudo firewall-cmd --reload
sudo semanage port -a -t postgresql_port_t -p tcp 5433
Conclusion
could not connect to server: Connection refused is a transport-layer rejection that happens before authentication: the host replied “nothing is listening here.” The usual root causes:
- PostgreSQL is not running (stopped or crashed).
listen_addressesdoes not include the interface the client targets.- The client is connecting to the wrong port (often a second cluster on 5433).
- A host or network firewall
rejectrule blocks the port. - The server is bound to localhost/the Unix socket only, so remote TCP is refused.
- A
postgresql.confport change was never applied, so config and running port differ.
Work it from the database host outward: confirm the service is up, check ss -ltnp for what is actually listening and where, then reconcile listen_addresses/port and the firewall with what the client expects. More PostgreSQL troubleshooting lives in the Postgres guides.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.