Skip to content
DevOps devops databases 6 min read

Configuring Remote Access

By default, PostgreSQL on Ubuntu only listens for connections coming from the same machine it runs on (this is called localhost, the loopback address 127.0.0.1). That is the safest setting, but sometimes your application server, your laptop, or a backup tool lives on a different machine and needs to talk to the database. To allow that, you have to change two settings and tell PostgreSQL who is allowed in. This page walks through exactly how to do that, and just as importantly, how to do it without leaving your database wide open to the internet.

The two files you will edit

PostgreSQL controls remote access with two configuration files. You need to understand what each one does before touching them.

FileWhat it controlsPlain-English meaning
postgresql.conflisten_addressesWhich network cards (IP addresses) the server listens on. Think of it as deciding which doors exist.
pg_hba.confHost-Based Authentication rulesWho is allowed through each door, from which IP, to which database, and how they must prove who they are. HBA stands for Host-Based Authentication.

On Ubuntu 22.04/24.04 LTS these files live under a version-specific path. PostgreSQL 16 is the current default, so the path is:

ls /etc/postgresql/16/main/

Output:

conf.d  environment  pg_ctl.conf  pg_hba.conf  pg_ident.conf  postgresql.conf

If you installed a different major version, swap 16 for your version number. You can confirm it with pg_lsclusters.

Step 1: Set listen_addresses

A network interface is just a connection point your server has to a network (for example, one for the public internet and one for a private internal network). listen_addresses tells PostgreSQL which of those it should accept connections on. Open the config file:

sudo nano /etc/postgresql/16/main/postgresql.conf

Find the line that starts with #listen_addresses. It looks like this by default:

#listen_addresses = 'localhost'		# what IP address(es) to listen on;

The # means the line is commented out (ignored), and the default behaviour is localhost only. Change it to listen on the specific private IP address of your server. Suppose your server’s private IP is 10.0.0.5:

listen_addresses = 'localhost,10.0.0.5'

Security warning: You will often see tutorials tell you to use listen_addresses = '*', which means “listen on every interface, including the public internet.” Do NOT do this unless a firewall is protecting the port. Binding to one specific private IP is far safer than *. Treat * as a last resort.

When to use which value:

ValueMeaningWhen to use
localhostSame machine onlyApp and DB on the same server. The safe default.
'localhost,10.0.0.5'Loopback plus one private IPApp is on another machine in the same private network. Recommended for remote access.
'*'Every interfaceOnly with a strict firewall in front. Risky.

Step 2: Add a host rule in pg_hba.conf

Now open the authentication file:

sudo nano /etc/postgresql/16/main/pg_hba.conf

Each rule is one line with these columns: TYPE DATABASE USER ADDRESS METHOD. We will add a host rule that lets a specific user connect to a specific database from a specific IP, using an encrypted password method called scram-sha-256 (Salted Challenge Response Authentication Mechanism — the modern, secure way PostgreSQL verifies passwords; it never sends the raw password over the wire).

Add this line, scoped as tightly as you can. Here we allow the user appuser to reach the database appdb only from the single application server IP 10.0.0.20:

# TYPE  DATABASE  USER      ADDRESS         METHOD
host    appdb     appuser   10.0.0.20/32    scram-sha-256

The /32 at the end is a CIDR netmask meaning “exactly this one IP address, nothing else” (CIDR is just a notation for describing a range of IPs). To allow a whole small private subnet instead, you could write 10.0.0.0/24, which covers 10.0.0.0 through 10.0.0.255.

Gotcha: Never write 0.0.0.0/0 here. That means “any IP address on Earth.” Combined with an open port, it invites every bot on the internet to brute-force your passwords. Always scope to a single host or a tight private subnet.

Make sure passwords use scram-sha-256

For the scram-sha-256 method to work, the password must be stored in that format. Check and, if needed, set the default:

sudo -u postgres psql -c "SHOW password_encryption;"

Output:

 password_encryption
----------------------
 scram-sha-256
(1 row)

If it shows md5 (an older, weaker method), set it to scram-sha-256 in postgresql.conf, restart, then re-set the user’s password with \password appuser inside psql so it is re-hashed.

Step 3: Restart PostgreSQL

Changes to listen_addresses require a full restart (a reload is not enough). Changes to pg_hba.conf only need a reload, but restarting covers both:

sudo systemctl restart postgresql
sudo systemctl status postgresql

Output:

● postgresql.service - PostgreSQL RDBMS
     Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled)
     Active: active (exited) since Mon 2026-06-15 10:12:04 UTC; 2s ago

Confirm PostgreSQL is now listening on the private IP and port 5432:

sudo ss -tlnp | grep 5432

Output:

LISTEN 0  244  127.0.0.1:5432  0.0.0.0:*  users:(("postgres",pid=2410,fd=7))
LISTEN 0  244  10.0.0.5:5432   0.0.0.0:*  users:(("postgres",pid=2410,fd=7))

Step 4: Lock the port down with a firewall

Opening the port in PostgreSQL is not the same as opening it on the operating system. Ubuntu ships with ufw (Uncomplicated Firewall). Allow port 5432 only from the trusted application server, and deny everything else:

sudo ufw allow from 10.0.0.20 to any port 5432 proto tcp
sudo ufw status

Output:

To                         Action      From
--                         ------      ----
5432/tcp                   ALLOW       10.0.0.20

This way, even if a rule were misconfigured, the firewall still blocks strangers.

Step 5: Test the connection

From the application server, test that you can reach the database. The psql client must be installed there too (sudo apt install postgresql-client):

psql "host=10.0.0.5 port=5432 dbname=appdb user=appuser sslmode=require"

Output:

Password for user appuser:
psql (16.3)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384)
Type "help" for help.

appdb=>

The sslmode=require part forces the connection to be encrypted in transit, so nobody on the network can read your traffic.

Prefer an SSH tunnel or private network

If your database does not truly need a public-facing port, the safest pattern is to not open one at all. Instead, use an SSH tunnel (a secure encrypted pipe through your existing SSH login). From your laptop:

ssh -L 5433:localhost:5432 user@your-server-ip

Now connect to localhost:5433 locally, and traffic is forwarded securely to PostgreSQL’s localhost:5432 on the server. PostgreSQL never needs listen_addresses changed and no extra port is exposed. Use this for admin access and one-off queries; use real remote access (Steps 1-4) only for app servers that connect constantly.

Best practices

  • Bind listen_addresses to a specific private IP, never '*' unless a firewall strictly fences the port.
  • Scope every pg_hba.conf rule to a single host (/32) or a tight private subnet. Never use 0.0.0.0/0.
  • Always use scram-sha-256, never trust or md5, for authentication.
  • Layer a ufw rule so the OS firewall also restricts port 5432 to known IPs.
  • Require SSL (sslmode=require or stronger) so credentials and data are encrypted in transit.
  • For occasional admin access, use an SSH tunnel instead of opening the database port to the network.
  • Keep a backup before changing config, and watch /var/log/postgresql/ for connection or authentication errors.
Last updated June 15, 2026
Was this helpful?