Start with the exact error
| Message | Meaning | Go to |
|---|---|---|
Connection refused | The host rejected the TCP connection; nothing is listening on that address and port | Layers 1–3 |
Connection timed out | No TCP response arrived; a firewall or network path may have dropped the packet | Layer 5 |
no pg_hba.conf entry for host | Postgres is running and reachable, but refuses this client | Layer 4 |
password authentication failed | You reached Postgres. Credentials are wrong. | Layer 6 |
could not connect to server: No such file or directory | A Unix-socket attempt used a path where no server socket exists | Layer 2 |
database "x" does not exist | Fully connected. Wrong database name. | Layer 6 |
Layer 1: is Postgres actually running?
# Linux service
sudo systemctl status postgresql
sudo systemctl start postgresql
# Homebrew on macOS
brew services list
brew services start postgresql@16
# Docker container
docker ps -a | grep postgres
docker logs <container>
If the service does not start, read its logs before changing connection settings. Common causes include another process using the port, incorrect data-directory permissions and recovery after an unclean shutdown.
sudo tail -50 /var/log/postgresql/postgresql-16-main.log # Debian or Ubuntu log
tail -50 /usr/local/var/log/postgresql@16.log # Homebrew log on macOS
Layer 2: is anything listening on 5432?
# Show the process bound to the port
sudo lsof -i :5432
sudo ss -tlnp | grep 5432
# Test whether the port is reachable
nc -zv localhost 5432
Look closely at the address in the output, not just the port:
127.0.0.1:5432 ← localhost only. Remote clients will be refused.
0.0.0.0:5432 ← all interfaces. Reachable from outside.
Multiple installations can obscure which server you are configuring. Homebrew and Postgres.app, or a local service and a container, may both try to use 5432. Check the process bound to each port; a second installed version may have moved to 5433.
Layer 3: listen_addresses
Postgres normally binds to localhost by default. To accept a remote TCP connection, edit the postgresql.conf used by the running process:
# Find the active configuration file
psql -U postgres -c 'SHOW config_file;'
# Settings for postgresql.conf
listen_addresses = '*' # Listen on every interface
# listen_addresses = 'localhost,10.0.1.5' # Bind only selected addresses
port = 5432
A listen_addresses change requires a restart, not only a reload. Use SHOW config_file to confirm the active file, especially on a machine with several PostgreSQL installations.
Layer 4: pg_hba.conf
pg_hba.conf controls client authentication and is evaluated top to bottom; the first matching rule wins. An error saying no pg_hba.conf entry for host confirms that the connection reached PostgreSQL but matched no permitted rule.
# Rule columns: TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
host all all 10.0.0.0/8 scram-sha-256 ← add your subnet
hostssl all all 0.0.0.0/0 scram-sha-256 ← require TLS for the internet
Reload the configuration after editing this file; a full restart is not required:
sudo systemctl reload postgresql
# Reload through SQL instead
psql -U postgres -c 'SELECT pg_reload_conf();'
For local connections, the peer method compares the operating-system user with the requested database role. On a fresh Ubuntu installation, psql mydb may work as your own user while psql -U postgres mydb fails because the shell user is not postgres. Use sudo -u postgres psql for that administrative role.
Layer 5: firewalls, security groups and the network
# Ubuntu firewall
sudo ufw allow from 10.0.0.0/8 to any port 5432
sudo ufw status
# RHEL or Fedora firewall
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --reload
- AWS RDS: the security group needs an inbound rule allowing port 5432 from the client source. Also check "Publicly accessible" when connecting over the public internet; that setting does not replace the security-group rule.
- Cloud SQL / Azure Database: add the client IP to the provider’s authorized network rules or connect through the supported proxy.
- Kubernetes: a
ClusterIPservice is reachable only from within the cluster. To test from your machine, runkubectl port-forward svc/postgres 5432:5432.
Layer 6: connection strings and credentials
postgresql://user:password@host:5432/dbname?sslmode=require
- Percent-encode special characters in the password. An
@can split the URL at the wrong point and produce a misleading host error.@is%40,#is%23, and/is%2F. localhostand127.0.0.1may resolve differently.localhostcan resolve to IPv6::1; a server listening only on IPv4 will refuse that connection. Test with the literal127.0.0.1.- Check
sslmode. Managed providers commonly requirerequireorverify-full. Client defaults vary: some prefer TLS and fall back, while others fail when their expected mode is unavailable.
The Docker special case
Inside a container, localhost refers to that container’s own network namespace. It does not refer to the host or to another Compose service.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
ports:
- "5432:5432" # Publish only for host access
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
api:
build: .
environment:
# Use the db service name instead of localhost
DATABASE_URL: postgresql://postgres:secret@db:5432/app
depends_on:
db:
condition: service_healthy
- Container to container: use the Compose service name (
db) and the container port (5432), regardless of the host port mapping. - Host to container: use
localhostwith the host side of the published port mapping. - Container to host machine: use
host.docker.internalon Docker Desktop or172.17.0.1on a typical Linux bridge. - Account for database startup time. Without
condition: service_healthy,depends_onstarts the application after the database container starts, not after PostgreSQL is ready for connections.
A one-pass diagnostic
# 1. Check whether the process is running
sudo systemctl status postgresql
# 2. Find the listening address
sudo ss -tlnp | grep 5432
# 3. Test the port from the client
nc -zv db-host 5432
# 4. Connect locally as the postgres system user
sudo -u postgres psql -c 'SELECT version();'
# 5. Bypass the Unix socket with an explicit TCP host
psql -h 127.0.0.1 -p 5432 -U myuser -d mydb
# 6. Read the active server settings
sudo -u postgres psql -c 'SHOW listen_addresses; SHOW port; SHOW hba_file;'
The first failed step narrows the layer to investigate. For example, step 4 succeeding while step 5 fails shows that the server is running but its TCP listener or pg_hba.conf rules need attention.
Frequently asked questions
What is the difference between connection refused and connection timed out?
Refused means the host returned an active TCP rejection because nothing accepted the connection on that address and port. Timed out means no response arrived, commonly because a firewall or network path dropped the traffic. Start with the listener for a refusal and the network path for a timeout.
Why does psql work locally but my application cannot connect?
psql without -h uses a Unix socket, which bypasses listen_addresses and matches a "local" line in pg_hba.conf. Your application uses TCP, which needs both listen_addresses to include the interface and a matching "host" line. Test with psql -h 127.0.0.1 to reproduce what your app is doing.
Why does my Docker container get connection refused to localhost?
Because inside a container, localhost is the container itself. Use the Compose service name to reach another container, or host.docker.internal to reach a service on your machine.
What does "no pg_hba.conf entry for host" mean?
The connection reached PostgreSQL, but no rule permits that combination of client address, database and role. Add an appropriately scoped host rule and reload the configuration; this change does not require a restart.
Do I need to restart Postgres after changing configuration?
pg_hba.conf changes need only a reload. listen_addresses, port and shared_buffers require a full restart. SHOW pending_restart in psql lists settings you have changed that are waiting for one.