PostgreSQL Install and Secure Setup on Linux (Roles, pg_hba.conf, TLS)

postgresql install secure setup - custom-pg-featured.png

PostgreSQL is a safe default database, but a default install is only half the job. Out of the box it listens on localhost and trusts local
OS users, which is fine until the day you open it to an application server. This guide installs PostgreSQL and then closes the usual gaps:
a dedicated non-superuser role, a tight pg_hba.conf, encrypted connections and sane memory settings.

Once it is running, schedule dumps with our PostgreSQL backup script and put a firewall from the
UFW guide in front of port 5432.

How Access Is Layered

Three independent layers decide who gets in: the network (firewall), the listen address, and pg_hba.conf authentication rules. A connection must pass all three.

architecturepg-layers.txt
  client --> firewall :5432 --> listen_addresses --> pg_hba.conf rule --> role + database
PostgreSQL layout
Clients, server, data directory

Install and Start the Server

Debian and Ubuntu initialise a cluster automatically. On AlmaLinux and Rocky you run postgresql-setup once.

bashDebian / Ubuntu
sudo apt install -y postgresql
sudo systemctl enable --now postgresql
bashAlmaLinux / Rocky
sudo dnf install -y postgresql-server
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql
bashcheck
sudo -u postgres psql -c 'SELECT version();'
Installing PostgreSQL
Packages, init, enable

Create a Least-Privilege Role and Database

Applications should never connect as postgres. Create a role that can log in but cannot create roles or databases, and give it its own database.

sqlas postgres
CREATE ROLE appuser WITH LOGIN PASSWORD 'CHANGE_ME_LONG_RANDOM' NOSUPERUSER NOCREATEDB NOCREATEROLE;
CREATE DATABASE appdb OWNER appuser;
REVOKE ALL ON DATABASE appdb FROM PUBLIC;
TipGenerate the password with openssl rand -base64 24 and keep it in a secrets manager such as Vault.
Roles and databases
App role owns app database

Set the Listen Address Deliberately

Leave listen_addresses on localhost if the app runs on the same host. If a remote app needs access, listen on the private interface only — never * on a public address.

inipostgresql.conf
listen_addresses = 'localhost,10.0.0.5'   # private IP only
port = 5432
bashfind the config path
sudo -u postgres psql -c 'SHOW config_file;'
listen_addresses
Network reachability

Write Strict pg_hba.conf Rules

Rules are read top to bottom and the first match wins. Allow the app subnet with scram-sha-256 and nothing else; an unmatched connection is rejected.

inipg_hba.conf
# TYPE   DATABASE  USER     ADDRESS        METHOD
local    all       postgres                peer
local    all       all                     scram-sha-256
hostssl  appdb     appuser  10.0.0.0/24    scram-sha-256
bashreload
sudo systemctl reload postgresql
pg_hba.conf
First match wins

Enable TLS for Client Connections

Without TLS, passwords are protected but data crosses the network readable. Point PostgreSQL at a certificate and use hostssl rules to require it.

inipostgresql.conf
ssl = on
ssl_cert_file = '/etc/ssl/pg/server.crt'
ssl_key_file  = '/etc/ssl/pg/server.key'
ssl_min_protocol_version = 'TLSv1.2'
WarningThe key file must be owned by postgres with mode 600 or the server refuses to start.
PostgreSQL TLS
Certificate, ssl=on, hostssl

Apply Baseline Memory Tuning

The defaults assume a tiny machine. As a starting point on a dedicated host, set shared_buffers near a quarter of RAM and effective_cache_size to about half to three quarters. Change one thing at a time and measure.

inipostgresql.conf (example for 16 GB RAM)
shared_buffers = 4GB
effective_cache_size = 10GB
work_mem = 16MB
maintenance_work_mem = 512MB
Baseline tuning
Buffers and cache hints

Verify Access and Prove a Restore

Test from the app host with TLS required, then take a dump and restore it into a scratch database. An unrestored backup is untested.

bashfrom the app host
psql "host=10.0.0.5 dbname=appdb user=appuser sslmode=require" -c 'SELECT 1;'
bashbackup + restore test
sudo -u postgres pg_dump -Fc appdb > /tmp/appdb.dump
sudo -u postgres createdb restore_test
sudo -u postgres pg_restore -d restore_test /tmp/appdb.dump && echo RESTORE OK
Verification
Connect, dump, restore

Quick Reference

  • Non-superuser app role; hostssl + scram-sha-256; private listen address
  • Tune from measurements; always test a restore

Related tutorials

Diagrams are original illustrations by Gnome IT Solutions. Tutorial text © Gnome IT Solutions.