PostgreSQL listens only on localhost after installation. Allowing another server or your laptop to connect takes two configuration changes — where Postgres listens (postgresql.conf) and who may authenticate from where (pg_hba.conf) — plus a firewall rule. Let’s do all three with security in mind.
Paths below are for Debian/Ubuntu (/etc/postgresql/<version>/main/). On RHEL-family systems they’re in the data directory, e.g. /var/lib/pgsql/data/. Find them with:
sudo -u postgres psql -c "SHOW config_file;" -c "SHOW hba_file;"
Option 0: SSH tunnel (no changes needed)
For admin access from your own machine, tunnel through SSH instead of opening the port:
ssh -N -L 5433:127.0.0.1:5432 [email protected]
psql -h 127.0.0.1 -p 5433 -U myapp myapp
pgAdmin, DBeaver and DataGrip all support SSH tunnels in their connection settings. See SSH port forwarding.
Step 1: listen on the right address
In postgresql.conf:
listen_addresses = 'localhost,10.0.0.5' # private/VPN IP — preferred
# listen_addresses = '*' # all interfaces — only with a strict firewall
This setting requires a restart, not just a reload:
sudo systemctl restart postgresql
sudo ss -tlnp | grep 5432
Step 2: allow the client in pg_hba.conf
pg_hba.conf is read top to bottom; the first matching line decides. Add a line for your client:
# TYPE DATABASE USER ADDRESS METHOD
host myapp myapp 10.0.0.6/32 scram-sha-256
host— TCP/IP connections (SSL or not). Usehostsslto require SSL.myapp myapp— only this database and this role.10.0.0.6/32— exactly one IP. A subnet like10.0.0.0/24is also possible.scram-sha-256— modern password authentication (default since PostgreSQL 14).
Never use trust for remote connections, and avoid all all 0.0.0.0/0.
Apply it — pg_hba.conf only needs a reload:
sudo systemctl reload postgresql
Step 3: make sure the password is SCRAM-hashed
If the role’s password was set before switching to SCRAM, it may be stored as MD5, and SCRAM authentication fails. Reset it:
sudo -u postgres psql -c "SHOW password_encryption;" # should be scram-sha-256
sudo -u postgres psql -c "\password myapp"
Step 4: firewall
sudo ufw allow from 10.0.0.6 to any port 5432 proto tcp
firewalld:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.6" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload
Step 5: test
From the client:
psql "host=10.0.0.5 port=5432 dbname=myapp user=myapp"
Step 6: SSL for internet-facing connections
Debian/Ubuntu packages enable SSL with a self-signed “snakeoil” certificate (ssl = on). For real protection against impersonation, install a certificate from a CA you trust (e.g. Let’s Encrypt for the server’s hostname, or your own private CA) and set:
ssl = on
ssl_cert_file = '/etc/postgresql/ssl/server.crt'
ssl_key_file = '/etc/postgresql/ssl/server.key'
Require it in pg_hba.conf with hostssl, and on the client use sslmode=verify-full with the CA certificate. sslmode=require encrypts but doesn’t verify the server’s identity.
Troubleshooting
could not connect to server: Connection refused — Postgres isn’t listening on that interface. Check listen_addresses and that you restarted.
Connection timed out — a firewall drops the traffic: UFW/firewalld on the server, or a provider-level firewall.
no pg_hba.conf entry for host "10.0.0.6", user "myapp", database "myapp", SSL off — no matching line. Check the IP (behind NAT it’s the public NAT address), database and user names, and whether the line requires SSL (hostssl).
password authentication failed for user "myapp" — wrong password, or MD5-stored password with SCRAM required. Reset with \password.
FATAL: Peer authentication failed — that’s a local socket connection using the peer method; add -h 127.0.0.1 locally. See peer authentication failed.
Check the server log for the exact reason:
sudo tail -n 50 /var/log/postgresql/postgresql-*-main.log
Connection pooling
Many remote clients opening many connections can exhaust PostgreSQL’s process-per-connection model. Put PgBouncer in front — see PgBouncer connection pooling.
Checklist
- SSH tunnel or VPN considered first
-
listen_addresseslimited to needed interfaces -
pg_hba.confline: specific database, role and /32 address,scram-sha-256 - Firewall allows only that client
-
hostssl+ verified certificates over the internet - No
trustentries for network connections
Related: PostgreSQL users and databases and installing PostgreSQL.