Encrypting client traffic with SSL (TLS) protects database credentials and query results from interception when connections traverse shared networks or untrusted infrastructure.
PostgreSQL negotiates TLS during the connection handshake when ssl is enabled and the server is configured with a certificate (server.crt) plus a private key (server.key). Clients opt in with parameters such as sslmode=require, while access rules in pg_hba.conf can explicitly permit only encrypted sessions by using hostssl entries.
Incorrect certificate paths, permissions, or pg_hba.conf rule ordering can prevent PostgreSQL from starting or can lock out remote clients. The private key must be readable by the PostgreSQL service account only, and production deployments should prefer a CA-trusted certificate plus strict client verification (sslmode=verify-full) instead of encryption-only modes.
Related: How to configure pg_hba.conf in PostgreSQL \\
Related: How to secure a PostgreSQL server
Steps to enable SSL for PostgreSQL connections:
- Open a terminal session on the database server with sudo privileges.
- Identify the active postgresql.conf, pg_hba.conf, and cluster data directory paths.
$ sudo -u postgres psql -Atc "SHOW config_file; SHOW hba_file; SHOW data_directory;" /etc/postgresql/16/main/postgresql.conf /etc/postgresql/16/main/pg_hba.conf /var/lib/postgresql/16/main
- Install the server certificate as server.crt in the cluster data directory.
$ sudo install --owner=postgres --group=postgres --mode=0644 server.crt /var/lib/postgresql/16/main/server.crt
Replace /var/lib/postgresql/16/main with the data directory path from the previous step.
- Install the server private key as server.key in the cluster data directory.
$ sudo install --owner=postgres --group=postgres --mode=0600 server.key /var/lib/postgresql/16/main/server.key
PostgreSQL refuses to start if server.key is group/world readable, commonly logging private key file "server.key" has group or world access.
- Enable SSL in the postgresql.conf file reported earlier.
ssl = on ssl_cert_file = 'server.crt' ssl_key_file = 'server.key'
Relative paths are resolved from the cluster data directory when ssl_cert_file and ssl_key_file are not absolute.
- Restart PostgreSQL to apply the SSL settings.
$ sudo systemctl restart postgresql
Enabling ssl and changing certificate/key paths require a restart.
- Confirm the server offers TLS by performing a STARTTLS handshake with OpenSSL.
$ openssl s_client -starttls postgres -connect 127.0.0.1:5432 Can't use SSL_get_servername depth=0 CN = host.example.net verify error:num=18:self-signed certificate verify return:1 ##### snipped ##### CONNECTED(00000003) ##### snipped ##### New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 ##### snipped ##### Verify return code: 18 (self-signed certificate)
A return code of 0 (ok) indicates a complete trusted certificate chain.
- Add a hostssl rule in pg_hba.conf to require TLS for remote clients.
hostssl appdb appuser 192.0.2.0/24 scram-sha-256 hostnossl appdb appuser 192.0.2.0/24 reject
Place the rules above any broader host entries that match the same clients, and validate scope carefully to avoid locking out legitimate access.
- Reload PostgreSQL to apply pg_hba.conf changes.
$ sudo -u postgres psql -c "SELECT pg_reload_conf();" pg_reload_conf ---------------- t (1 row)
- Verify an SSL-required connection from a client using psql.
$ psql "host=app.internal.example port=5432 dbname=appdb user=appuser sslmode=require" -c "\conninfo" You are connected to database "appdb" as user "appuser" on host "app.internal.example" (address "127.0.0.1") at port "5432". SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
sslmode=require encrypts traffic but does not validate server identity, so use sslmode=verify-full with a trusted root certificate for protection against man-in-the-middle attacks.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
