Key Takeaways
- Harden PostgreSQL and Redis on Ubuntu 24.04 with TLS, SCRAM, ACLs, pgaudit, network isolation, and encrypted backups.
- Learn which settings are worth locking down first, and how to verify configuration with real commands.
- Use role-based access, host-level firewall rules, and local certificate management to minimize blast radius.
- This guide is written for practitioners who want reproducible hardening steps, not just theory.
Direct Answer: Harden self-hosted databases by enabling TLS for PostgreSQL and Redis, enforcing ACL-based access controls, logging SQL activity with pgaudit, isolating database traffic across dedicated networks, and storing backups encrypted with GPG. The sections below include Ubuntu 24.04 commands, example configuration blocks, and verification steps for each hardening layer.
Why this matters
Databases are the crown jewels of any self-hosted stack, and PostgreSQL plus Redis are often the two backend services that attackers probe first. In a sovereign setup, the goal is not only to keep data on your hardware, but also to make sure the service is hardened enough that a compromised app or network segment cannot trivially pivot to your data.
For 2026, this means a practical hardening baseline:
- encrypt database traffic with TLS so credentials and queries are not exposed on the wire
- lock down access with least-privilege roles and Redis ACLs
- capture audit trails for sensitive actions with
pgaudit - isolate database traffic on dedicated subnets and host firewalls
- keep backups encrypted and verifiable so stolen snapshots are useless
Threat model
This guide is written for environments where the attacker may already control an untrusted host on the same LAN, or where an application credential can be leaked. It assumes the database servers themselves are trusted hardware, but not the surrounding network.
Key risk vectors:
- hostile network segment or internal lateral movement
- compromised application credentials
- unauthorized database administrative access
- stolen backups or snapshot exfiltration
Each section below adds a dependable layer to reduce those risks.
PostgreSQL hardening approach
The PostgreSQL portion of this guide focuses on practical, repeatable system hardening on Ubuntu 24.04:
- install PostgreSQL and
pgaudit - enable TLS with a local CA
- enforce SCRAM authentication and host-level restrictions
- create dedicated app roles with only the privileges they need
- verify the service is listening securely and the audit logs are generated
1. Install PostgreSQL and required extensions
sudo apt update
sudo apt install -y postgresql-15 postgresql-server-dev-15 postgresql-contrib
Enable pgaudit by adding it to shared preloads:
sudo tee /etc/postgresql/15/main/postgresql.conf > /dev/null <<'EOF'
listen_addresses = 'localhost'
shared_preload_libraries = 'pgaudit'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%a.log'
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a '
log_statement = 'none'
audit_logging = 'all'
audit_role = 'postgres'
password_encryption = scram-sha-256
ssl = on
ssl_cert_file = '/etc/ssl/certs/postgres.crt'
ssl_key_file = '/etc/ssl/private/postgres.key'
ssl_ca_file = '/etc/ssl/certs/ca.crt'
ssl_prefer_server_ciphers = on
ssl_ciphers = 'TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384'
EOF
2. Generate TLS certificates for PostgreSQL
sudo mkdir -p /etc/ssl/postgres
sudo chmod 700 /etc/ssl/postgres
cd /etc/ssl/postgres
sudo openssl genpkey -algorithm RSA -out postgres.key -pkeyopt rsa_keygen_bits:4096
sudo openssl req -new -key postgres.key -out postgres.csr -subj '/CN=postgres.example.local'
sudo openssl x509 -req -in postgres.csr -signkey postgres.key -out postgres.crt -days 365
sudo cp postgres.crt /etc/ssl/certs/postgres.crt
sudo cp postgres.key /etc/ssl/private/postgres.key
sudo chmod 600 /etc/ssl/private/postgres.key
Reload PostgreSQL:
sudo systemctl restart postgresql
3. Restrict client authentication with pg_hba.conf
Use mTLS and SCRAM authentication for application clients.
sudo tee /etc/postgresql/15/main/pg_hba.conf > /dev/null <<'EOF'
# local connections from Unix socket
local all postgres peer
local all all scram-sha-256
# application subnet
hostssl all all 10.10.0.0/24 scram-sha-256
# management host
hostssl all postgres 192.168.1.10/32 scram-sha-256
EOF
Reload config and verify:
sudo systemctl reload postgresql
sudo -u postgres psql -c "SELECT name, setting FROM pg_settings WHERE name IN ('ssl', 'ssl_prefer_server_ciphers');"
Expected output excerpt:
name | setting
-------------+-----------------------
ssl | on
ssl_prefer_server_ciphers | on
If you see off, the most common causes are wrong certificate paths or a permissions issue on /etc/ssl/private/postgres.key. PostgreSQL won’t enable SSL until it can read the key securely.
4. Create least-privilege roles
sudo -u postgres psql <<'SQL'
CREATE ROLE app_user LOGIN PASSWORD 'S3cureP@ssw0rd!' NOSUPERUSER INHERIT;
CREATE DATABASE appdb OWNER app_user;
GRANT CONNECT ON DATABASE appdb TO app_user;
\c appdb
CREATE SCHEMA app AUTHORIZATION app_user;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
SQL
5. Validate audit logging
sudo -u postgres psql -c "SELECT * FROM pg_extension WHERE extname='pgaudit';"
sudo tail -n 8 /var/lib/postgresql/15/main/pg_log/postgresql-*.log
A working audit log should include entries like AUDIT: SESSION and AUDIT: ROLE. For example:
AUDIT: SESSION,1,1,LOG,SELECT,SESSION,app_user,appdb,1,0,0,0,0,0,0,0,0,0,LOG,SELECT 1
AUDIT: ROLE,1,1,LOG,SET,SESSION,app_user,appdb,1,0,0,0,0,0,0,0,0,0,SET ROLE app_user
If audit records are missing, confirm that shared_preload_libraries = 'pgaudit' is present and PostgreSQL has been fully restarted.
6. Limit connection sources and ports
Use UFW to allow only trusted hosts and application subnets.
sudo apt install -y ufw
sudo ufw allow from 10.10.0.0/24 to any port 5432 proto tcp comment 'App network'
sudo ufw allow from 192.168.1.10 to any port 5432 proto tcp comment 'Management host'
sudo ufw deny 5432/tcp
sudo ufw enable
sudo ufw status verbose
Redis Hardening
1. Install Redis with native TLS support
sudo apt update
sudo apt install -y redis-server openssl
Edit /etc/redis/redis.conf and apply minimum exposure.
bind 127.0.0.1 10.10.0.5
protected-mode yes
port 0
tls-port 6379
tls-cert-file /etc/ssl/certs/redis.crt
tls-key-file /etc/ssl/private/redis.key
tls-ca-cert-file /etc/ssl/certs/ca.crt
tls-auth-clients yes
aclfile /etc/redis/users.acl
supervised systemd
2. Generate Redis TLS certificates
sudo mkdir -p /etc/ssl/redis
sudo chmod 700 /etc/ssl/redis
dir=/etc/ssl/redis
sudo openssl genpkey -algorithm RSA -out $dir/redis.key -pkeyopt rsa_keygen_bits:4096
sudo openssl req -new -key $dir/redis.key -out $dir/redis.csr -subj '/CN=redis.example.local'
sudo openssl x509 -req -in $dir/redis.csr -signkey $dir/redis.key -out $dir/redis.crt -days 365
sudo cp $dir/redis.crt /etc/ssl/certs/redis.crt
sudo cp $dir/redis.key /etc/ssl/private/redis.key
sudo chmod 600 /etc/ssl/private/redis.key
3. Configure ACLs and disable dangerous commands
Create an ACL file that restricts clients and blocks unsafe commands.
sudo tee /etc/redis/users.acl > /dev/null <<'EOF'
user default on nopass ~* +@all -DEBUG -FLUSHDB -FLUSHALL -CONFIG -SCRIPT -SHUTDOWN -SWAPDB
user app on >S3cureR3disP@ss ~* +get +set +expire +del +exists +ttl
EOF
Restart Redis:
sudo systemctl restart redis-server
4. Test Redis TLS connection and ACL enforcement
redis-cli --tls --cacert /etc/ssl/certs/ca.crt --cert /etc/ssl/certs/redis.crt --key /etc/ssl/private/redis.key -h 127.0.0.1 -p 6379 ACL WHOAMI
Expected output:
1) "app"
This confirms the TLS session completed and the ACL user is recognized correctly. If the command fails with NOAUTH, verify the certificate pair and tls-auth-clients yes setting in redis.conf.
Also test blocked commands:
redis-cli --tls --cacert /etc/ssl/certs/ca.crt -h 127.0.0.1 -p 6379 -a 'S3cureR3disP@ss' FLUSHALL
Expected output:
(error) NOPERM this user has no permission to run 'flushall'
If you see a different error, check /var/log/redis/redis-server.log for ACL parsing failures or invalid user definitions.
Encrypted backups and recovery
PostgreSQL backup with GPG encryption
sudo apt install -y gpg
sudo -u postgres pg_dump -Fc appdb > /tmp/appdb.dump
gpg --batch --yes --output /tmp/appdb.dump.gpg --encrypt --recipient "[email protected]" /tmp/appdb.dump
Validate the encrypted file:
gpg --list-packets /tmp/appdb.dump.gpg
file /tmp/appdb.dump.gpg
Redis snapshot backup
sudo systemctl stop redis-server
sudo cp /var/lib/redis/dump.rdb /tmp/dump.rdb
sudo systemctl start redis-server
gpg --batch --yes --output /tmp/dump.rdb.gpg --encrypt --recipient "[email protected]" /tmp/dump.rdb
Recovery commands
gpg --decrypt /tmp/appdb.dump.gpg | sudo -u postgres pg_restore -d appdb
sudo gpg --decrypt /tmp/dump.rdb.gpg > /var/lib/redis/dump.rdb
sudo chown redis:redis /var/lib/redis/dump.rdb
sudo systemctl restart redis-server
Validation and verification
PostgreSQL service health
sudo -u postgres pg_isready -h 127.0.0.1 -p 5432
Expected output:
127.0.0.1:5432 - accepting connections
Redis TLS check
redis-cli --tls --cacert /etc/ssl/certs/ca.crt -h 127.0.0.1 -p 6379 PING
Expected output:
PONG
Security hardening checklist
- TLS enabled for PostgreSQL and Redis
- Role-based access control for PostgreSQL
- Redis ACL file with blocked commands
- Audit logging enabled with
pgaudit - Firewall restricts database ports to application and admin hosts
- Backups encrypted with GPG and integrity validated
Real deployment notes
- Use
pg_isreadyandredis-cli --tls ... PINGfrom a staging host to verify the application subnet can reach the services. - Prefer separate PostgreSQL and Redis storage volumes with filesystem encryption at rest.
- Keep the certificate issuer and expiry dates documented in your runbook so renewal does not become a fire drill.
Troubleshooting
Postgres still listening on 0.0.0.0
Check listen_addresses in /etc/postgresql/15/main/postgresql.conf. Use sudo ss -tlnp | grep 5432 to confirm the bind address.
Redis TLS handshake failures
Verify certificate matching and tls-auth-clients yes. Use openssl s_client -connect 127.0.0.1:6379 -CAfile /etc/ssl/certs/ca.crt to inspect the TLS flow.
pgaudit log entries not appearing
Confirm shared_preload_libraries = 'pgaudit' and restart PostgreSQL. If the extension is missing, install it with sudo -u postgres psql -c "CREATE EXTENSION pgaudit;".
People Also Ask
How do I enforce least privilege for PostgreSQL applications?
Create dedicated database roles with NOSUPERUSER, grant only the required schema and table privileges, revoke default public privileges, and use SCRAM-SHA-256 passwords or certificate authentication. Avoid shared users and never connect applications as postgres.
Can Redis be secured without TLS?
Redis can be partially secured with bind, requirepass, and ACLs, but for sovereign infrastructure it’s best practice to use TLS to protect data in transit and avoid simple network eavesdropping on unencrypted Redis traffic.
What is the minimum secure Redis setup for 2026?
Disable the non-TLS port, enable tls-port, set tls-auth-clients yes, use an ACL file, block dangerous commands, and keep Redis behind a host-level firewall and isolated application subnet.
Further Reading
- Docker Private Registry 2026 — store container images for sovereign infrastructure workflows
- Edge Computing Guide 2026 — deploy edge AI and local search with secure data pipelines
- Best Local Embedding Models 2026 — secure vector search and on-prem embedding deployment
- GitOps with Argo CD on K3s 2026 — declarative GitOps for self-hosted deployment pipelines
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Last verified: May 2, 2026.