Updated: 28 September 2026 · Applies to: Ubuntu 24.04, Debian 12, AlmaLinux 9 and Rocky Linux 9
In a three-tier application (web server, application server, database), the links between the tiers should be encrypted and not reachable from the internet. SSH tunnels are one way to do this. This guide shows the idea with a Rails application and PostgreSQL on Ubuntu 24.04, Debian 12, AlmaLinux 9 and Rocky Linux 9.
A tunnel from the application server to the database
# on the application server, as the app user (key login to the database server must work) ssh -N -L 127.0.0.1:5433:127.0.0.1:5432 tunnel@db.example.com
The application now connects to 127.0.0.1:5433 (in config/database.yml: host: 127.0.0.1, port: 5433), and PostgreSQL on the database server only needs to listen on its own localhost.
Keep the tunnel running
Run it as a systemd service with autossh so it restarts automatically:
[Unit] Description=SSH tunnel to database After=network-online.target [Service] User=deploy ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes -L 127.0.0.1:5433:127.0.0.1:5432 tunnel@db.example.com Restart=always [Install] WantedBy=multi-user.target
Restrict the tunnel user on the database server: in ~/.ssh/authorized_keys prefix its key with restrict,port-forwarding,permitopen="127.0.0.1:5432", so the key can only open that one tunnel.
Alternatives
- A private network between your servers, where available, with the database listening only on the private IP.
- WireGuard between all tiers: one encrypted network for every service.
- TLS in the database itself: PostgreSQL and MariaDB support encrypted client connections (
sslmode=verify-fullin PostgreSQL clients).