A reverse SSH tunnel lets you access a machine behind NAT or a restrictive firewall — one that can't normally accept inbound connections — by having that machine initiate an outbound connection to a publicly reachable server instead.
The Problem This Solves
A machine on a home network or behind a corporate firewall typically can't be reached directly from the internet. A reverse tunnel flips the direction: the hard-to-reach machine connects out to your VPS, which then acts as a relay point you can connect through.
Basic Concept
[Hard-to-reach machine] —(outbound SSH connection)→ [Your VPS] —(you connect here)→ [access relayed back to the machine]
Prerequisites
- A VPS with a public IP, reachable from both ends
- SSH access from the hard-to-reach machine to the VPS
Step 1 — Establish the Reverse Tunnel (Run on the Hard-to-Reach Machine)
ssh -R 2222:localhost:22 tunnel-user@YOUR_VPS_IP -N
This forwards port 2222 on the VPS to port 22 (SSH) on the local machine running this command. -N means don't execute a remote command, just establish the tunnel.
Step 2 — Configure the VPS to Accept the Tunnel
sudo nano /etc/ssh/sshd_config
GatewayPorts yes
sudo systemctl restart sshd
GatewayPorts yes allows the forwarded port to be reachable from outside the VPS itself, not just from localhost on the VPS — needed if you'll be connecting from a third machine, not the VPS itself.
Step 3 — Connect Through the Tunnel
From anywhere, connect to the VPS on the forwarded port to reach the remote machine:
ssh -p 2222 user@YOUR_VPS_IP
Making the Tunnel Persistent (autossh)
A manually-run tunnel drops if the connection is interrupted. Use autossh to automatically reconnect:
sudo apt install autossh -y
autossh -M 0 -R 2222:localhost:22 tunnel-user@YOUR_VPS_IP -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3"
Running the Tunnel as a systemd Service (On the Remote Machine)
sudo nano /etc/systemd/system/reverse-tunnel.service
[Unit]
Description=Reverse SSH Tunnel
After=network.target
[Service]
ExecStart=/usr/bin/autossh -M 0 -R 2222:localhost:22 tunnel-user@YOUR_VPS_IP -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3"
Restart=always
User=tunneluser
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now reverse-tunnel
Securing the Tunnel User Account
Create a dedicated, restricted account on the VPS specifically for accepting this tunnel connection — see How to Set Up a Deployment User with Restricted SSH Access for the general pattern of a minimal-privilege account, applied here to the tunnel user rather than a deployment context.
Forwarding Other Services (Not Just SSH)
ssh -R 8080:localhost:80 tunnel-user@YOUR_VPS_IP -N
Forwards a web service running on port 80 of the remote machine, reachable via port 8080 on the VPS.
Common Errors
Tunnel connects but the forwarded port isn't reachable externally — verify GatewayPorts yes is set and sshd was restarted.
Tunnel drops periodically — use autossh with keepalive options rather than a plain ssh command, which doesn't automatically reconnect after a drop.
Best Practices
- Use a dedicated, restricted account for the tunnel connection, not a general-purpose one
- Use autossh with keepalive settings for reliability rather than a manually-run tunnel
- Restrict which ports/services are forwarded to only what's genuinely needed
Continue Reading
- How to Set Up a Deployment User with Restricted SSH Access
- How to Secure SSH: Key-Based Authentication & Disabling Root Login
- How to Set Up a VPN Server with WireGuard
Browse more articles in Advanced Networking & VPN.
