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-full in PostgreSQL clients).

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket