Understanding the PostgreSQL 'Connection refused' Error
The Connection refused error in PostgreSQL occurs when a client application attempts to connect to the database server but the TCP/IP connection is actively rejected. Instead of timing out, the server (or the operating system) sends an immediate rejection response. This is fundamentally different from a Connection timed out error, and understanding that distinction is the first step toward a successful fix.
What Exactly Does "Connection refused" Mean?
At the network level, a Connection refused error means that the client sent a TCP SYN packet to a specific IP address and port, but received a TCP RST (reset) packet in response. This indicates that no process is actively listening on that port, or that a firewall is explicitly rejecting the connection attempt rather than silently dropping it. In PostgreSQL terms, this typically translates to one of the following scenarios:
- PostgreSQL is not running at all
- PostgreSQL is running but not listening on the expected IP address or port
- A firewall (iptables, ufw, firewalld, or cloud security group) is explicitly rejecting traffic
- The
listen_addressesconfiguration does not include the client's network - PostgreSQL is listening on a Unix socket only, and the client is attempting a TCP connection
Why This Error Matters
A Connection refused error can bring your entire application stack to a halt. Whether you're deploying a web application, running data pipelines, or managing a containerized microservice architecture, database connectivity is the backbone of persistence. The error often surfaces at the worst possible moment — during a production deployment, after a system reboot, or following a configuration change. Knowing how to systematically diagnose and resolve it is an essential skill for any developer or DevOps engineer working with PostgreSQL.
Step-by-Step Diagnosis
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Before applying fixes, you must determine the exact cause. Follow this diagnostic sequence to narrow down the problem efficiently.
1. Verify That PostgreSQL Is Running
On Linux systems using systemd, check the service status with:
# Check PostgreSQL service status
systemctl status postgresql
# Or for specific versions
systemctl status postgresql@14-main
systemctl status postgresql-14
On systems using init.d scripts or direct process checking:
# Check for running PostgreSQL processes
ps aux | grep postgres
# Look for the postmaster process - this is the main PostgreSQL process
ps aux | grep postmaster
If PostgreSQL is not running, you'll see an inactive service status or no matching processes. In that case, start it immediately:
# Start PostgreSQL using systemd
sudo systemctl start postgresql
# Or start a specific version
sudo systemctl start postgresql@14-main
# Enable it to start on boot
sudo systemctl enable postgresql
2. Confirm the Listening Port and Address
Even if PostgreSQL is running, it may not be listening on the port or interface you expect. Use ss or netstat to inspect listening sockets:
# Show all listening TCP sockets with process information
sudo ss -tlnp | grep postgres
# Alternative using netstat
sudo netstat -tlnp | grep 5432
# Check for both IPv4 and IPv6 listeners
sudo ss -tlnp | grep -E '5432|postgres'
A healthy TCP-listening PostgreSQL server should show output similar to:
LISTEN 0 244 0.0.0.0:5432 0.0.0.0:* users:(("postgres",pid=1234,fd=7))
LISTEN 0 244 [::]:5432 [::]:* users:(("postgres",pid=1234,fd=8))
If you see only a Unix socket entry like /var/run/postgresql/.s.PGSQL.5432, then PostgreSQL is listening exclusively on a Unix socket — TCP clients will get a Connection refused error.
3. Test Connectivity from the Client Machine
Use basic networking tools to determine if the issue is at the network layer or the application layer:
# Basic TCP connectivity test using netcat
nc -zv [host] 5432
# Or using telnet
telnet [host] 5432
# Using the psql client with explicit connection parameters
psql -h [host] -p 5432 -U [username] -d [database]
A successful nc -zv will report Connection to [host] 5432 port [tcp/postgresql] succeeded!. If you get Connection refused here, the problem is definitely at the network or service level, not an authentication issue.
4. Inspect PostgreSQL Configuration Files
Two configuration files are critical for connectivity: postgresql.conf and pg_hba.conf. Locate them first:
# Find the configuration files - run as the postgres user or with sudo
sudo -u postgres psql -c "SHOW config_file;"
sudo -u postgres psql -c "SHOW hba_file;"
# Alternatively, search the filesystem
sudo find /etc -name postgresql.conf 2>/dev/null
sudo find /var -name postgresql.conf 2>/dev/null
Common Causes and Their Solutions
Cause 1: PostgreSQL Not Running or Crashed
Symptoms: systemctl status postgresql shows inactive or failed state. No postgres or postmaster processes visible.
Solution: Start the service and investigate logs for crash reasons:
# Start PostgreSQL
sudo systemctl start postgresql
# Check logs for crash information
sudo journalctl -u postgresql -n 50 --no-pager
sudo tail -f /var/log/postgresql/postgresql-14-main.log
# Common crash causes and fixes:
# - Insufficient shared memory: increase kernel.shmmax and kernel.shmall
# - Disk full: free up space
# - Corrupted data directory: restore from backup or run pg_resetwal (last resort)
Cause 2: listen_addresses Not Properly Configured
Symptoms: PostgreSQL is running but only listening on 127.0.0.1 (localhost). Remote clients get Connection refused.
Solution: Modify listen_addresses in postgresql.conf:
# Open postgresql.conf with your preferred editor
sudo nano /etc/postgresql/14/main/postgresql.conf
# Find and modify the listen_addresses line
# Default (localhost only):
listen_addresses = 'localhost'
# Change to listen on all interfaces:
listen_addresses = '*'
# Or specify explicit comma-separated addresses:
listen_addresses = '192.168.1.100,10.0.0.5'
# Restart PostgreSQL to apply the change
sudo systemctl restart postgresql
# Verify the change took effect
sudo ss -tlnp | grep postgres
sudo -u postgres psql -c "SHOW listen_addresses;"
Cause 3: Wrong Port Number
Symptoms: PostgreSQL is running on a non-default port (not 5432), but the client is connecting to port 5432.
Solution: Verify the actual port in postgresql.conf and update client connection strings:
# Check configured port
sudo grep -E '^port\s*=' /etc/postgresql/14/main/postgresql.conf
# Check with SQL
sudo -u postgres psql -c "SHOW port;"
# If the port has been changed, update your connection strings:
# psql command line:
psql -h [host] -p [actual_port] -U [username] -d [database]
# In application connection strings, use the correct port:
# PostgreSQL JDBC: jdbc:postgresql://host:[actual_port]/database
# libpq: host=hostname port=[actual_port] dbname=database
Cause 4: Firewall Explicitly Rejecting Connections
Symptoms: PostgreSQL is running and listening on the correct interface and port, but external clients still get Connection refused. Local connections work fine.
Solution: Configure the firewall to allow PostgreSQL traffic. The approach depends on your firewall system:
# ---- iptables (direct rules) ----
# Check existing rules
sudo iptables -L -n -v | grep 5432
# Add a rule to allow PostgreSQL (insert at appropriate position)
sudo iptables -I INPUT -p tcp --dport 5432 -j ACCEPT
# Save rules (varies by distribution)
sudo iptables-save > /etc/iptables/rules.v4
# Or on some systems:
sudo service iptables save
# ---- ufw (Uncomplicated Firewall, common on Ubuntu) ----
# Check status
sudo ufw status
# Allow PostgreSQL port
sudo ufw allow 5432/tcp
sudo ufw reload
# ---- firewalld (common on RHEL/CentOS/Fedora) ----
# Add a permanent rule
sudo firewall-cmd --permanent --add-service=postgresql
# Or by port:
sudo firewall-cmd --permanent --add-port=5432/tcp
# Reload to apply
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
Cause 5: Cloud Security Group / Network ACL Blocking Traffic
Symptoms: In cloud environments (AWS, GCP, Azure), connections from external IPs are refused even though the OS firewall is open.
Solution: Update the cloud provider's security group or firewall rules:
# AWS EC2 Security Group example (via AWS CLI):
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxx \
--protocol tcp \
--port 5432 \
--cidr 203.0.113.0/24 \
--description "Allow PostgreSQL from application servers"
# GCP firewall rule example (via gcloud):
gcloud compute firewall-rules create allow-postgresql \
--network=default \
--allow=tcp:5432 \
--source-ranges=10.0.0.0/8 \
--description="Allow PostgreSQL from internal network"
# Azure Network Security Group rule (via az CLI):
az network nsg rule create \
--nsg-name myNsg \
--resource-group myResourceGroup \
--name AllowPostgreSQL \
--priority 1000 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--destination-port-range 5432
Cause 6: pg_hba.conf Rejecting Client Connections
Symptoms: The TCP connection succeeds (you can telnet to port 5432 and see a response), but psql or the application reports an error like no pg_hba.conf entry for host or Connection refused after the initial handshake. While the primary error message here is often different, some client libraries conflate authentication rejections with connection refusals.
Solution: Add an appropriate entry to pg_hba.conf:
# Open pg_hba.conf
sudo nano /etc/postgresql/14/main/pg_hba.conf
# Add a line for your client network (examples):
# Allow MD5 password authentication from a specific subnet
host all all 192.168.1.0/24 md5
# Allow scram-sha-256 (more secure) from a specific IP
host all all 10.0.0.100/32 scram-sha-256
# Allow trust authentication (no password - only for development!)
host all all 172.16.0.0/12 trust
# Reload configuration without restarting (if using signal)
sudo -u postgres psql -c "SELECT pg_reload_conf();"
# Or restart
sudo systemctl restart postgresql
# Verify the change
sudo -u postgres psql -c "SELECT * FROM pg_hba_file_rules;"
Cause 7: SELinux Blocking PostgreSQL Network Access
Symptoms: On RHEL/CentOS/Fedora systems with SELinux enforcing, PostgreSQL may be running and listening but connections are refused due to SELinux policies restricting network access.
Solution: Check SELinux status and adjust policies:
# Check if SELinux is enforcing
getenforce
# Temporarily set to permissive to test if SELinux is the cause
sudo setenforce 0
# If connections work now, re-enable enforcing and fix the policy:
sudo setenforce 1
# Check for SELinux denials related to PostgreSQL
sudo ausearch -m AVC -ts recent | grep postgres
sudo grep postgres /var/log/audit/audit.log | grep denied
# Allow PostgreSQL to bind to its default port
sudo semanage port -a -t postgresql_port_t -p tcp 5432
# If PostgreSQL is using a non-standard port
sudo semanage port -a -t postgresql_port_t -p tcp [custom_port]
# Allow PostgreSQL to connect to the network
sudo setsebool -P postgresql_can_network_connect on
Cause 8: Docker/Container Networking Misconfiguration
Symptoms: PostgreSQL runs in a Docker container, and connections from the host or other containers are refused.
Solution: Verify port mapping and container networking:
# Check if the container is running and port mapping is correct
docker ps | grep postgres
# Inspect port mappings
docker inspect [container_name] | grep -A 10 HostPort
# Common Docker run command with proper port mapping:
docker run -d \
--name postgres-container \
-p 5432:5432 \
-e POSTGRES_PASSWORD=securepassword \
postgres:16
# If using Docker Compose, ensure ports are mapped:
# docker-compose.yml excerpt:
# services:
# postgres:
# image: postgres:16
# ports:
# - "5432:5432"
# For container-to-container communication, use Docker network:
docker network create app-network
docker run -d --network app-network --name postgres postgres:16
docker run -d --network app-network --name app myapp:latest
# Then connect from app container using hostname "postgres"
Quick Diagnostic Script
Here is a comprehensive diagnostic script you can run on the PostgreSQL server to quickly identify the root cause:
#!/bin/bash
# PostgreSQL Connection Diagnostic Script
# Run with sudo for complete results
echo "=== PostgreSQL Connection Diagnostic ==="
echo ""
# 1. Service status
echo "--- Service Status ---"
if systemctl is-active postgresql &>/dev/null; then
echo "✓ PostgreSQL service is active"
elif systemctl is-active postgresql@* &>/dev/null; then
echo "✓ PostgreSQL versioned service is active"
else
echo "✗ PostgreSQL service is NOT active"
fi
# 2. Listening ports
echo ""
echo "--- Listening Ports ---"
LISTENERS=$(sudo ss -tlnp | grep postgres 2>/dev/null)
if [ -n "$LISTENERS" ]; then
echo "✓ PostgreSQL is listening:"
echo "$LISTENERS"
else
echo "✗ No PostgreSQL listeners found"
fi
# 3. listen_addresses configuration
echo ""
echo "--- listen_addresses Configuration ---"
CONF_FILE=$(sudo -u postgres psql -c "SHOW config_file;" -t 2>/dev/null | tr -d ' ')
if [ -n "$CONF_FILE" ]; then
LISTEN_ADDR=$(sudo grep -E '^listen_addresses\s*=' "$CONF_FILE" 2>/dev/null)
echo "Config file: $CONF_FILE"
echo "listen_addresses: $LISTEN_ADDR"
else
echo "Could not determine config_file"
fi
# 4. Firewall rules
echo ""
echo "--- Firewall Rules ---"
if command -v ufw &>/dev/null; then
sudo ufw status | grep 5432 || echo "No ufw rule for port 5432"
elif command -v firewall-cmd &>/dev/null; then
sudo firewall-cmd --list-all | grep 5432 || echo "No firewalld rule for port 5432"
elif command -v iptables &>/dev/null; then
sudo iptables -L -n | grep 5432 || echo "No iptables rule for port 5432"
fi
# 5. Test local connection
echo ""
echo "--- Local Connection Test ---"
if sudo -u postgres psql -c "SELECT 1;" &>/dev/null; then
echo "✓ Local psql connection successful"
else
echo "✗ Local psql connection FAILED"
fi
echo ""
echo "=== Diagnostic Complete ==="
Best Practices to Prevent 'Connection refused' Errors
1. Implement Proactive Monitoring
Set up monitoring that continuously checks PostgreSQL connectivity from the perspective of your application servers. Use tools like pg_isready, which is specifically designed for this purpose:
# Check if PostgreSQL is accepting connections
pg_isready -h [host] -p 5432 -U [username] -d [database]
# Integrate into monitoring scripts
# Exit codes: 0=success, 1=failure (server down), 2=failure (no response)
# Use in cron jobs or systemd timers for regular checks
2. Use Connection Pooling with Health Checks
Connection poolers like PgBouncer or Pgpool-II can mask transient connection issues and provide graceful retry logic. Configure health checks to detect connection refusals early:
# PgBouncer configuration excerpt (pgbouncer.ini):
[databases]
mydb = host=localhost port=5432 dbname=mydb
[pgbouncer]
listen_addr = *
listen_port = 6432
auth_type = scram-sha-256
# Health check query
query_wait_timeout = 120
3. Document Configuration Changes
Every change to postgresql.conf, pg_hba.conf, firewall rules, or network topology should be version-controlled and documented. Use configuration management tools like Ansible, Chef, or Terraform to enforce consistency across environments.
4. Implement Graceful Connection Retry Logic
In your application code, implement retry logic with exponential backoff for initial connection attempts. A server restart or brief network flap should not cause a permanent failure:
# Python example using psycopg2 with retry logic
import psycopg2
import time
from psycopg2 import OperationalError
def connect_with_retry(dsn, max_retries=5, backoff_factor=2):
for attempt in range(max_retries):
try:
conn = psycopg2.connect(dsn)
print("Connection established successfully")
return conn
except OperationalError as e:
if 'Connection refused' in str(e):
wait_time = backoff_factor ** attempt
print(f"Connection refused, retrying in {wait_time}s...")
time.sleep(wait_time)
else:
raise
raise Exception(f"Failed to connect after {max_retries} retries")
# Usage
dsn = "host=dbhost port=5432 dbname=appdb user=appuser password=secret"
conn = connect_with_retry(dsn)
5. Use Unix Sockets for Local Connections When Possible
For applications running on the same machine as PostgreSQL, Unix socket connections bypass TCP entirely, avoiding firewall issues and reducing latency:
# Local connection via Unix socket (default on most systems)
psql -U username -d database
# In application connection strings, omit the host parameter or use a socket path:
# libpq: host=/var/run/postgresql dbname=mydb
# psycopg2: conn = psycopg2.connect("dbname=mydb user=user")
# JDBC: jdbc:postgresql:///mydb (note the triple slash)
6. Maintain a Failover and Recovery Plan
For production environments, implement replication and automatic failover using tools like Patroni, repmgr, or cloud-managed solutions. A standby server that automatically promotes when the primary becomes unreachable prevents prolonged Connection refused outages.
Conclusion
The PostgreSQL Connection refused error is a deceptively simple message that can stem from a wide range of root causes — from an inactive service to a misconfigured cloud security group. The key to resolving it efficiently is systematic diagnosis: verify the service status, inspect listening sockets, test network connectivity, review configuration files, and check firewall rules in order. By following the step-by-step approach outlined in this tutorial, you can identify and fix the underlying issue in minutes rather than hours. Equally important is implementing the preventive best practices: proactive monitoring with pg_isready, connection retry logic, configuration version control, and proper firewall management. With these tools and habits in place, you will dramatically reduce the frequency and impact of connection failures, ensuring your PostgreSQL-backed applications remain reliable and resilient.