Kingsfordweg 151, 1043GR Amsterdam, The Netherlands ● 24/7 support — support@novogara.com
Knowledge base

SSH tunnels

Before you open a port in the firewall to reach something, consider that SSH can already carry it and you have the credentials.

Local forwarding: bring a remote service to you

ssh -L 5433:localhost:5432 user@server

Now localhost:5433 on your laptop is the PostgreSQL on the server. The database stays bound to localhost, no firewall rule is needed, and the connection is encrypted by SSH.

This is the right way to reach a database, a Redis instance or an admin interface for an occasional look. See connecting your own servers for the permanent arrangement.

To reach a third machine the server can see:

ssh -L 8080:10.0.0.9:80 user@server

The second host and port are resolved from the server's point of view, which is the part people get backwards.

Dynamic forwarding: a SOCKS proxy

ssh -D 1080 user@server

Point your browser at SOCKS5 on localhost:1080 and everything goes out through the server. Convenient for reaching several internal interfaces at once without a tunnel each.

For regular use, WireGuard is better: faster, survives a dropped connection, and routes whole networks rather than one browser.

Remote forwarding: the one to be careful with

ssh -R 9000:localhost:3000 user@server

Makes a service on your machine reachable from the server. Handy for showing someone a local development build. By default it binds to the server's localhost only; GatewayPorts would expose it more widely, and you almost never want that.

Keeping it up

In ~/.ssh/config:

Host server
  ServerAliveInterval 30
  ServerAliveCountMax 3
  ExitOnForwardFailure yes

That last one matters: without it, a tunnel whose port is already in use fails silently and you spend twenty minutes wondering why you are talking to the wrong thing.

For a tunnel that must stay up, run it under systemd with Restart=always and -N (no shell) rather than in a screen session — see running your application as a service.

Jump hosts

ssh -J bastion user@internal

Connects through a bastion in one command, with no agent forwarding and no key sitting on the intermediate machine. Prefer this to ssh -A: agent forwarding lets anyone with root on the intermediate host use your key while you are connected.

Restrict what a key can do

For an automated tunnel, give it a dedicated key limited in authorized_keys:

restrict,port-forwarding,permitopen="10.0.0.9:5432" ssh-ed25519 AAAA...

That key can forward to one host and port and do nothing else — no shell, no other destinations. See SSH access and users and sudo.


Still stuck? Mail support@novogara.com — an engineer answers, at any hour. Back to the knowledge base