Technology Encyclopedia Home >How to Configure a Reverse SSH Tunnel for Remote Access — Access Anything Behind a NAT

How to Configure a Reverse SSH Tunnel for Remote Access — Access Anything Behind a NAT

I have a home server that does various things — file backup, network monitoring, a few small services. It's on a residential internet connection behind a NAT router, so there's no public IP to connect to directly. Configuring port forwarding on the router would work, but it's fragile and the IP changes.

A reverse SSH tunnel solves this cleanly: the home server establishes an outbound SSH connection to a cloud server, and that connection creates a tunnel I can reach from anywhere. The home server only needs outbound internet access — no open inbound ports, no router configuration.

This guide covers setting up the tunnel, making it persistent with autossh, and handling the common issue where the tunnel drops after inactivity.

The technique: the device behind the NAT connects outward to your cloud server (outbound connections are almost never blocked). The cloud server then relays incoming connections back through that tunnel.

I use this on Tencent Cloud Lighthouse as the relay server. The entry-level plan is more than sufficient — a reverse SSH tunnel uses minimal bandwidth and CPU. What matters for this use case is reliability: Lighthouse instances have a static public IP that doesn't change, which is important for the home server or Raspberry Pi that establishes the outbound connection to know where to connect. The server's continuous uptime also means the tunnel can be maintained persistently without worrying about the relay going offline.


Table of Contents

  1. How Reverse SSH Tunnels Work
  2. Prerequisites
  3. Part 1 — Set Up the Cloud Server
  4. Part 2 — Configure the Tunnel from the Local Device
  5. Part 3 — Connect Through the Tunnel
  6. Part 4 — Make the Tunnel Persistent with autossh
  7. Part 5 — Run the Tunnel as a systemd Service
  8. Part 6 — Tunnel Multiple Services
  9. The Gotcha: GatewayPorts Configuration
  10. Practical Use Cases

  • Key Takeaways
  • Use the appropriate Lighthouse application image to skip manual installation steps where available
  • Lighthouse snapshots provide one-click full-server backup before major changes
  • OrcaTerm browser terminal lets you manage the server from any device
  • CBS cloud disk expansion handles growing storage needs without server migration
  • Console-level firewall + UFW = two independent protection layers

How Reverse SSH Tunnels Work {#how}

Home Device (behind NAT)          Cloud Server (public IP)
  └─ outbound SSH connection ──→  └─ listens on public port
       (connects to cloud)              (e.g., 2222)

From anywhere on the internet:
  └─ SSH to cloud:2222 ──────────→  tunneled to home device:22

Normal SSH: you connect from your laptop to the server (inbound).
Reverse SSH tunnel: the server connects back to your laptop's location.

The key insight: outbound connections from a device behind NAT work fine. Inbound connections (from the internet directly to the home device) are blocked by the router. A reverse tunnel turns that outbound connection into an access path.


Prerequisites {#prerequisites}

Requirement Notes
Cloud server Tencent Cloud Lighthouse with a public IP
Local device Any Linux/macOS/Windows machine you want to access remotely
SSH key pair For passwordless authentication

Part 1 — Set Up the Cloud Server {#part-1}

Enable GatewayPorts

By default, SSH only allows forwarded ports to be accessible from localhost. To make the tunnel accessible from the internet, enable GatewayPorts:

ssh ubuntu@YOUR_SERVER_IP
sudo nano /etc/ssh/sshd_config

Add or update:

GatewayPorts yes
sudo systemctl restart sshd

Open the tunnel port in the firewall

sudo ufw allow 2222/tcp   # The port we'll use for remote SSH access
sudo ufw allow ssh        # Make sure management SSH stays open

Also open port 2222 in the Lighthouse console firewall tab.

Create a tunnel user (optional but recommended)

sudo adduser tunneluser --disabled-password --shell /usr/sbin/nologin

A dedicated user with no shell login limits what the tunnel connection can do on the server.


Part 2 — Configure the Tunnel from the Local Device {#part-2}

On the device behind NAT (home server, Raspberry Pi, etc.):

Generate SSH keys (if not already done)

ssh-keygen -t ed25519 -C "tunnel-key"
# Save to default location
cat ~/.ssh/id_ed25519.pub

Copy the public key to the cloud server

ssh-copy-id ubuntu@YOUR_SERVER_IP
# Or for the tunnel user:
# ssh-copy-id tunneluser@YOUR_SERVER_IP

Open the reverse tunnel manually (test first)

# This creates a reverse tunnel:
# Port 2222 on the cloud server → port 22 on this local device
ssh -R 2222:localhost:22 ubuntu@YOUR_SERVER_IP -N -f

# -R 2222:localhost:22 = bind port 2222 on the remote server to localhost:22 on the local machine
# -N = don't execute a command
# -f = go to background

Verify the tunnel is working:

# From any machine on the internet, SSH to the cloud server on port 2222
# This should connect you to the local device's SSH
ssh -p 2222 localuser@YOUR_SERVER_IP

If you get a shell on your home device through the cloud server — the tunnel works!


Part 3 — Connect Through the Tunnel {#part-3}

From any internet-connected machine:

# Connect to home device via the cloud server relay
ssh -p 2222 homeuser@YOUR_SERVER_IP

This connects you to homeuser on your home device, tunneled through the cloud server.

Using a jump host config

Add to ~/.ssh/config on your laptop:

Host home-server
    HostName YOUR_SERVER_IP
    Port 2222
    User homeuser
    IdentityFile ~/.ssh/id_ed25519

Then just:

ssh home-server

Part 4 — Make the Tunnel Persistent with autossh {#part-4}

The simple ssh -R command disconnects if the network drops. autossh monitors the connection and reconnects automatically:

# On the home device:
sudo apt install -y autossh

# Start persistent reverse tunnel
autossh -M 0 \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -o "ExitOnForwardFailure yes" \
  -N -f \
  -R 2222:localhost:22 \
  ubuntu@YOUR_SERVER_IP

-M 0 disables autossh's built-in monitoring (uses SSH keepalive instead).


Part 5 — Run the Tunnel as a systemd Service {#part-5}

On the local device, create a systemd service that starts the tunnel on boot and restarts it if it fails:

sudo nano /etc/systemd/system/reverse-tunnel.service
[Unit]
Description=Reverse SSH Tunnel to Cloud Server
After=network.target

[Service]
User=YOUR_LOCAL_USER
ExecStart=/usr/bin/autossh -M 0 \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -o "ExitOnForwardFailure yes" \
  -o "StrictHostKeyChecking no" \
  -N \
  -i /home/YOUR_LOCAL_USER/.ssh/id_ed25519 \
  -R 2222:localhost:22 \
  ubuntu@YOUR_SERVER_IP
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable reverse-tunnel
sudo systemctl start reverse-tunnel
sudo systemctl status reverse-tunnel

The tunnel now starts on boot and reconnects automatically if the connection drops.


Part 6 — Tunnel Multiple Services {#part-6}

You can forward multiple ports in a single SSH command:

ssh -R 2222:localhost:22 \      # SSH access
    -R 8080:localhost:80 \       # Web server
    -R 3306:localhost:3306 \     # MySQL (only accessible from cloud server)
    ubuntu@YOUR_SERVER_IP -N

Each -R argument adds another forwarded port.

Tunnel a web service

Forward a web server running on your home device to the cloud server:

-R 8080:localhost:3000

On the cloud server, configure Nginx to proxy yourdomain.com to localhost:8080:

location / {
    proxy_pass http://localhost:8080;
}

This makes your home web server accessible at https://yourdomain.com — even though it's behind NAT.

Restrict tunnel ports (security)

To limit forwarded ports to be accessible only from localhost on the cloud server (not from the internet directly):

In sshd_config, use GatewayPorts clientspecified instead of GatewayPorts yes. Then in the SSH command, bind to localhost explicitly:

-R localhost:2222:localhost:22

This makes port 2222 accessible only from the cloud server itself, not from the internet. You'd then SSH to the cloud server and from there SSH internally to localhost:2222.


The Gotcha: GatewayPorts Configuration {#gotcha}

Without GatewayPorts yes on the cloud server, reverse-forwarded ports bind to 127.0.0.1 only — accessible from the cloud server itself but not from the internet.

Symptoms: tunnel appears to be running, but ssh -p 2222 ubuntu@YOUR_SERVER_IP times out.

Debug:

# On the cloud server
sudo ss -tlnp | grep 2222
# Should show: 0.0.0.0:2222 (all interfaces) if GatewayPorts is enabled
# Shows:       127.0.0.1:2222 (localhost only) if GatewayPorts is disabled

Fix: add GatewayPorts yes to /etc/ssh/sshd_config on the cloud server and restart sshd.


Practical Use Cases {#use-cases}

Scenario Setup
Access home server SSH -R 2222:localhost:22
Expose home web server -R 8080:localhost:80 + Nginx proxy
Access home NAS web UI -R 5001:nas-ip:5001
Remote desktop (VNC) -R 5900:localhost:5900
Database access -R 3307:localhost:3306 (MySQL)
Local dev to the internet -R 3000:localhost:3000 + Nginx proxy

Troubleshooting {#troubleshooting}

Issue Likely Cause Fix
Connection refused Service not running or wrong port Check systemctl status SERVICE and verify firewall rules
Permission denied Wrong file ownership or permissions Check file ownership with ls -la and use chown/chmod to fix
502 Bad Gateway Backend service not running Restart the backend service; check logs with journalctl -u SERVICE
SSL certificate error Certificate expired or domain mismatch Run sudo certbot renew and verify domain DNS points to server IP
Service not starting Config error or missing dependency Check logs with journalctl -u SERVICE -n 50 for specific error
Out of disk space Logs or data accumulation Run df -h to identify usage; clean logs or attach CBS storage
High memory usage Too many processes or memory leak Check with htop; consider upgrading instance plan if consistently high
Firewall blocking traffic Port not open in UFW or Lighthouse console Open port in Lighthouse console firewall AND sudo ufw allow PORT

Frequently Asked Questions {#faq}

How does reverse SSH tunnel differ from a simple SSH tunnel?
SSH tunnels are connection-specific and not persistent without extra tools. reverse SSH tunnel is a dedicated service that runs continuously, supports multiple protocols, and provides a more robust and manageable solution for ongoing tunnel needs.

Is the tunnel traffic encrypted?
Traffic through the Lighthouse server itself can be encrypted depending on the protocol used. For sensitive applications, use TLS/SSL on top of the tunnel for end-to-end encryption.

How do I make the tunnel persistent after server reboots?
Set up both the server and client components as systemd services with Restart=on-failure. They'll start automatically on boot and recover from crashes.

What is the performance overhead of using reverse SSH tunnel?
Minimal for most use cases. Latency adds the round-trip time through the relay server. Throughput is limited by the Lighthouse server's bandwidth and network speed.

Can I expose services on non-standard ports?
Yes — reverse SSH tunnel supports TCP/UDP port forwarding on any port. Open the corresponding ports in the Lighthouse console firewall and UFW.

Set up your relay server today:
👉 Tencent Cloud Lighthouse — Reliable cloud server for SSH relay
👉 View current pricing and promotions
👉 Explore all active deals and offers