In today’s data-driven world, having control over your database infrastructure is becoming increasingly important. While cloud-based database services offer convenience, self-hosting PostgreSQL gives you complete control over your data, configuration, and security measures.
Are you concerned about database security when exposing PostgreSQL to the internet? You’re right to be cautious. Unencrypted database connections can expose sensitive data to interception, potentially leading to data breaches. Fortunately, with proper SSL implementation, you can create a secure, publicly accessible PostgreSQL server that maintains data confidentiality and integrity.
This guide will walk you through the process of setting up a PostgreSQL server on Debian, securing it with SSL certificates, and configuring it for secure remote access. We’ll use Let’s Encrypt for SSL certificates and implement automatic renewal using certbot hooks to ensure your database remains secure with minimal maintenance.
By the end of this tutorial, you’ll have:
Before we begin, ensure you have:
Let’s start by installing PostgreSQL from the official PostgreSQL repository to ensure we get the latest version:
# Install prerequisites
sudo apt install curl ca-certificates
# Add the PostgreSQL signing key
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
# Add the PostgreSQL repository
. /etc/os-release
sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
# Update package lists
sudo apt update
# Install PostgreSQL 17 and related packages
sudo apt -y install postgresql-17 postgresql-contrib-17
This installation method ensures you get the latest PostgreSQL version directly from the official PostgreSQL repository, rather than using the potentially older version in the Debian repositories.
Verify the installation by checking the service status:
sudo systemctl status postgresql@17-main
You should see that the service is active and running.
Before we configure SSL, let’s perform some basic setup steps.
Switch to the postgres user and access the PostgreSQL command-line interface:
sudo -i -u postgres
psql -p 5432
Within the PostgreSQL prompt, set a strong password for the postgres user:
ALTER USER postgres WITH PASSWORD 'your_secure_password';
For better security practices, create a dedicated database and user for your application:
CREATE DATABASE myappdb;
CREATE USER myappuser WITH ENCRYPTED PASSWORD 'another_secure_password';
GRANT ALL PRIVILEGES ON DATABASE myappdb TO myappuser;
Exit the PostgreSQL prompt:
\q
By default, PostgreSQL only listens on localhost. We need to modify its configuration to accept external connections and use SSL.
Edit the PostgreSQL configuration file:
sudo nano /etc/postgresql/17/main/postgresql.conf
Find and modify the following parameters:
listen_addresses = '*' # Listen on all interfaces
ssl = on # Enable SSL
ssl_cert_file = 'fullchain.pem' # Let's Encrypt certificate
ssl_key_file = 'privkey.pem' # Let's Encrypt private key
Next, configure client authentication by editing the pg_hba.conf file:
sudo nano /etc/postgresql/17/main/pg_hba.conf
Add the following lines at the end of the file to allow SSL connections:
# Allow SSL connections from any IP for specific users
hostssl all myappuser 0.0.0.0/0 md5
hostssl all postgres 0.0.0.0/0 md5
# Optional: Allow local connections without SSL
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
This configuration:
We’ll use Let’s Encrypt to generate free, trusted SSL certificates and set up automatic renewal with a custom hook script to update PostgreSQL certificates.
First, install certbot:
sudo apt install certbot
Generate the initial certificates:
sudo certbot certonly --standalone -d postgres.mydomain.com
Now, create a renewal hook script that will automatically update the PostgreSQL certificates when Let’s Encrypt renews them:
sudo nano /etc/letsencrypt/renewal-hooks/deploy/postgresql-cert-update.sh
Add the following script:
#!/bin/bash
DOMAIN="postgres.mydomain.com"
PG_VERSION="17"
PGDATA="/var/lib/postgresql/$PG_VERSION/main"
CERT_PATH="/etc/letsencrypt/live/$DOMAIN"
FULLCHAIN_FILE="$CERT_PATH/fullchain.pem"
PRIVKEY_FILE="$CERT_PATH/privkey.pem"
TEMP_FULLCHAIN="$PGDATA/fullchain.pem-1"
TEMP_PRIVKEY="$PGDATA/privkey.pem-1"
FINAL_FULLCHAIN="$PGDATA/fullchain.pem"
FINAL_PRIVKEY="$PGDATA/privkey.pem"
/usr/sbin/apache2ctl graceful
cp "$FULLCHAIN_FILE" "$TEMP_FULLCHAIN"
cp "$PRIVKEY_FILE" "$TEMP_PRIVKEY"
chmod 600 "$TEMP_FULLCHAIN" "$TEMP_PRIVKEY"
chown postgres:postgres "$TEMP_FULLCHAIN" "$TEMP_PRIVKEY"
mv -f "$TEMP_FULLCHAIN" "$FINAL_FULLCHAIN"
mv -f "$TEMP_PRIVKEY" "$FINAL_PRIVKEY"
pg_ctlcluster "$PG_VERSION" main reload
The last line of the script uses pg_ctlcluster
with the reload
parameter instead of restart
. This is an important distinction:
reload
tells PostgreSQL to re-read its configuration files without disconnecting clients or interrupting operationsrestart
, which would terminate all connections and potentially disrupt applicationsMake the script executable:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/postgresql-cert-update.sh
Run the script once to copy the initial certificates to the PostgreSQL data directory:
sudo /etc/letsencrypt/renewal-hooks/deploy/postgresql-cert-update.sh
Restart PostgreSQL to apply all changes:
sudo systemctl restart postgresql@17-main
If you’re using a firewall (which you should), allow connections to the PostgreSQL port:
# For UFW
sudo ufw allow 5432/tcp
# For iptables
sudo iptables -A INPUT -p tcp --dport 5432 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4
From another machine, test the connection using the PostgreSQL client:
psql "host=postgres.mydomain.com port=5432 dbname=myappdb user=myappuser sslmode=require"
If everything is configured correctly, you should be prompted for a password and then connected to the database.
For production environments, consider additional security measures like:
reload
parameter updates PostgreSQL configuration without disconnecting clientspg_hba.conf
) determines which connections require SSLYou now have a secure PostgreSQL 17 server running on Debian, accessible remotely via SSL-encrypted connections. The setup includes automatic certificate renewal through Let’s Encrypt and certbot hooks, ensuring your SSL certificates are always up-to-date.
Remember that database security is an ongoing process. Regularly update your system, review access logs, and adjust security measures as needed to protect your valuable data.
By following these practices, you’ll have a robust, secure PostgreSQL deployment that meets the needs of your applications while keeping your data safe from unauthorized access.
If you’re having trouble connecting, check:
sudo tail -f /var/log/postgresql/postgresql-17-main.log
openssl x509 -in /var/lib/postgresql/17/main/fullchain.pem -text -noout
If certificate renewal isn’t working:
sudo journalctl -u certbot
sudo certbot renew --dry-run