How to use the SSH Tunnel Generator
- Choose local (-L), remote (-R) or SOCKS (-D) forwarding.
- Enter the listening port and address, and for -L or -R the target host and port.
- Enter the SSH server, user, port and key, and choose -N, -f and ExitOnForwardFailure.
- Copy the ssh command, or install the systemd unit to keep the tunnel up across reboots.
What is an SSH tunnel?
An SSH tunnel, or SSH port forward, carries TCP connections for one port inside an encrypted SSH connection. ssh -L 5432:localhost:5432 deploy@server makes port 5432 on your computer lead to port 5432 on the server, so you can reach a database that is not open to the internet at all.
There are three kinds, all described in the ssh(1) man page:
| Flag | Listens on | Connects to | Typical use |
|---|---|---|---|
-L local | your computer | a host and port as the server sees them | a database or admin panel kept private on the server |
-R remote | the server | a host and port as your computer sees them | showing a local dev server, reaching a machine behind NAT |
-D dynamic | your computer | wherever each application asks (SOCKS) | browsing as if from the server |
How each part of the command works
In -L 127.0.0.1:5432:db.internal:5432 the first address and port are where ssh listens on your computer, and db.internal:5432 is resolved on the server. So localhost as the target means the server itself, not your machine. With -R it is the other way round: the port opens on the server and the target is resolved on your computer.
- -N runs no remote command; the connection exists only for the forward.
- -f sends ssh to the background once it has logged in. It needs
-N(or a command), or ssh refuses. - ExitOnForwardFailure=yes makes ssh quit when it cannot open the port, for example because something already listens there. Without it, ssh stays connected and the tunnel silently does nothing.
- ServerAliveInterval sends a keepalive every so many seconds, so a dead connection is noticed and a NAT router does not drop an idle one.
Binding: 127.0.0.1 or 0.0.0.0
The bind address decides who can use the tunnel. 127.0.0.1 (the default this tool writes) lets only programs on the same machine connect. 0.0.0.0 or * lets anyone who can reach that machine use it, so they can reach the target through your SSH login too. For a SOCKS proxy that makes it an open proxy.
For -R, the server decides. sshd's GatewayPorts setting defaults to no, which binds every remote forward to the server's loopback whatever you ask for. To publish a remote forward on the server's public address, set GatewayPorts clientspecified in the server's sshd_config and give a bind address, then firewall the port.
Ports below 1024 are privileged. On Linux, only root can listen there unless net.ipv4.ip_unprivileged_port_start is lowered, and sshd forwards a remote port below 1024 only for a login as root. Use a high port and point a proxy at it instead.
Worked examples
# Reach Postgres on the server from this computer, in the background
ssh -N -f -o ExitOnForwardFailure=yes -L 127.0.0.1:5432:localhost:5432 deploy@203.0.113.10
psql -h 127.0.0.1 -p 5432 -U postgres
# Let the server reach a dev server on your laptop at its own port 9000
ssh -N -R 9000:localhost:3000 deploy@203.0.113.10
# SOCKS proxy: traffic leaves from the server
ssh -N -D 127.0.0.1:1080 deploy@203.0.113.10
curl --socks5-hostname 127.0.0.1:1080 https://example.comKeeping a tunnel up
A plain ssh -f dies with the network and does not come back. Two ways to keep it:
- autossh restarts ssh when it exits.
autossh -M 0turns off its own monitoring port and relies onServerAliveIntervalto notice a dead link. - A systemd unit runs
ssh -Nin the foreground withRestart=always, starts it at boot and logs to the journal. It setsBatchMode=yesso ssh never waits for a password or a host-key prompt, which means you connect once by hand, as the account the unit runs as, to accept the server's host key first.
Common errors and how to fix them
- bind [127.0.0.1]:5432: Address already in use. Something on your computer already listens on that port, often a local Postgres. Pick another local port, such as 15432.
- channel 2: open failed: connect failed: Connection refused. The tunnel works, but nothing listens on the target host and port as the server sees them. Check with
ss -tlnpon the server. - Warning: remote port forwarding failed for listen port. The port is taken on the server, is below 1024 for a non-root login, or
AllowTcpForwardingis off in itssshd_config. - The -R port answers only on the server.
GatewayPortsisnoon the server; see above.
Shipown and private ports
A tunnel lets you keep a database's port closed to the internet. Shipown's server management lists the ports that are actually listening next to your ufw or firewalld rules, so you can see that 5432 is not open to everyone before you rely on a tunnel to reach it.
Frequently asked questions
What is the difference between ssh -L and ssh -R?
ssh -L listens on your computer and connects out from the server. ssh -R listens on the server and connects back to a host your computer can reach.
Why does my reverse tunnel only work from the server itself?
The server’s sshd has GatewayPorts no, its default, which binds remote forwards to loopback. Set GatewayPorts clientspecified in sshd_config and give a bind address such as 0.0.0.0 to publish it.
How do I keep an SSH tunnel running after a reboot?
Run it as a systemd service with Restart=always, or with autossh started at boot. The unit runs ssh -N in the foreground with BatchMode=yes, so it never waits for input.
Is an SSH SOCKS proxy the same as a VPN?
No. ssh -D proxies only the applications you point at it, over TCP, while a VPN routes the whole machine. For browsing or a few tools, a SOCKS proxy is often enough.
Why use 127.0.0.1 instead of 0.0.0.0 for a tunnel?
127.0.0.1 lets only programs on the same machine use the tunnel. 0.0.0.0 lets anyone who can reach that machine use it, and through it, your SSH access.
Can I forward a port below 1024?
Only as root: Linux reserves ports below 1024 for root, and sshd forwards a remote port below 1024 only for a root login. Forward a high port and point a proxy at it instead.