SKDBLOG

DevOps

How to Enable PostgreSQL Remote Connections on Ubuntu (Safely)

Postgres ships listening on localhost for good reason. When you really need a remote client, coordinate postgresql.conf, pg_hba.conf, service restarts, and UFW the same way you would for Redis or MongoDB—tight rules first, party second.

Shashikant Dwivedi
6 min read
How to Enable PostgreSQL Remote Connections on Ubuntu (Safely)
DevOps06 MIN

Hey there—if you already walked through a fresh PostgreSQL install on Ubuntu, the next scary step is letting a real client reach the daemon without handing your data to the entire internet. Postgres defaults to localhost-only listeners because that posture survives contact with reality.

You are aiming for five minutes of focused edits: widen listen_addresses, teach pg_hba.conf which subnets may authenticate, restart, prove port 5432 is listening, then mirror the firewall rhythm I keep beside the Ubuntu nginx command paste board whenever UFW enters the story.

⏱️ Estimated time: 5–10 minutes

Security: exposing database ports

Opening database ports to the public internet is convenient for demos and risky in production. Prefer VPN, SSH tunnels, or IP allow-lists, enforce TLS where available, and use strong users and network rules.

Before you change anything

You need:

  • PostgreSQL installed and running (reuse the install post above if this is a green box)
  • sudo access on the server
  • The server’s routable IP or hostname, plus the client IP you intend to allow
  • A database role that may log in remotely once networking cooperates

If you like side-by-side stories, Redis remote access on Ubuntu shows the same pattern—widen bind, restrict who reaches the port, scream-test with systemd and logs.

Open postgresql.conf

Tell Postgres which interfaces may accept sockets by editing the main config:

bash
sudo nano /etc/postgresql/[version]/main/postgresql.conf

Replace [version] with your cluster (for example 15 or 16). Snapshot the file first if production traffic depends on it.

Set listen_addresses

Find the listener directive:

bash
listen_addresses = 'localhost'

Update it when you intend remote clients:

bash
listen_addresses = '*'

'*' means every interface Postgres knows about—not “trust everyone,” just “listen everywhere.” Tighten the blast radius with pg_hba.conf, firewall rules, and VPC boundaries instead of dreaming that localhost alone will save you.

Save and exit (Ctrl + X, then Y, then Enter in nano).

Configure client authentication in pg_hba.conf

Authentication is a separate file:

bash
sudo nano /etc/postgresql/[version]/main/pg_hba.conf

Append rules that mirror your actual networks—prefer explicit client IPs rather than 0.0.0.0/0.

Allow a single trusted workstation or bastion (recommended):

conf
host    all             all             your_client_ip/32         md5

Allow every IPv4 address (last resort demos only):

conf
host    all             all             0.0.0.0/0                md5

Swap your_client_ip for the address that should dial in. Keep in mind that order matters: Postgres stops at the first matching pg_hba.conf line, so park the narrowest rules above broad ones.

Restart PostgreSQL and verify

Reload the service so both files take effect:

bash
sudo service postgresql restart

Check health:

bash
sudo service postgresql status

Confirm something is listening where you expect:

bash
sudo netstat -tuln | grep 5432

You want 0.0.0.0:5432 (or the interface IP you targeted) rather than only 127.0.0.1:5432. Modern boxes also expose the same signal through ss -tuln | grep 5432 if netstat feels archaic on your image.

Open port 5432 in UFW (carefully)

If UFW protects the host, opening TCP 5432 follows the same muscle memory as other services—list apps, allow only what you need, then print verbose status. When I do not want to retype the UFW cadence from scratch, I keep the nginx-focused snippet nearby because it already documents the ufw allow / ufw status verbose loop I paste beside database work.

bash
sudo ufw allow 5432/tcp

Tighten further with a source IP whenever your cloud provider or team policy allows:

bash
sudo ufw allow from your_client_ip to any port 5432 proto tcp

MongoDB and friends tell the same story—compare with MongoDB remote connections if you want a parallel checklist for another engine.

Troubleshooting

When remote clients still fail:

  • Logs: sudo tail -f /var/log/postgresql/postgresql-[version]-main.log surfaces syntax mistakes and auth rejections quickly.
  • Config typos: a stray quote in postgresql.conf or pg_hba.conf prevents reloads from doing what you think.
  • Firewall layers: UFW might be open while a cloud security group still blocks 5432, or the client might be NATed to a different public IP than you allow-listed.
  • Roles: confirm the Postgres user exists, may authenticate, and uses the password you expect from the remote machine—not localhost on the client.

Frequently asked questions

Is it safe to set listen_addresses to '*'?

Star means every network interface can accept connections; the risk is not Postgres binding widely, it is weak pg_hba.conf or firewall rules. Pair wide bind with strict IP allow lists, VPN paths, or SSH tunnels so only intended clients reach port 5432.

What is the difference between postgresql.conf and pg_hba.conf?

postgresql.conf decides where the server listens. pg_hba.conf decides which client IPs, databases, and users may authenticate and which auth methods apply—both are required before remote tools succeed.

Why does this walkthrough use md5 in pg_hba.conf?

md5 password challenge-response still appears in older tutorials and clusters; Postgres now prefers scram-sha-256 for new installs. The flow is identical—swap the method to scram-sha-256 once all clients support it.

I allowed port 5432 in UFW—why does my client still time out?

Confirm Postgres restarted, netstat or ss shows 5432 listening on 0.0.0.0 or your IP, pg_hba.conf includes your client subnet, no cloud security group is blocking, and you targeted the server’s address rather than localhost from the remote host.

Should I expose PostgreSQL directly to the public internet?

Only for narrow demos. Production traffic should land on VPNs, private subnets, bastion SSH tunnels, or application tiers that proxy—never raw 5432 to the world without layered controls.

You should now have Postgres listening where you intend, authenticating the clients you listed, and guarded by the same firewall instincts you use everywhere else on the box. When TLS termination or HTTP-shaped routing enters the picture, layer nginx reverse proxy setup in front of the apps—not necessarily raw SQL—so the edge policy stays coherent.

Written by Shashikant Dwivedi

Engineer, occasional writer, full-time noticer. Based in Prayagraj, India. New essays land roughly twice a month.

Keep reading

Adjacent essays.

All writing →

The newsletter

New articles in your inbox.

Occasional articles on engineering, tooling, and software development practices. No marketing, no fluff — just the article, when it's ready.

Unsubscribe with one click. Your email never leaves the list.