termal.in

← Blog

SSH into a server behind NAT or CGNAT, without port forwarding

· Termalin Team sshtunnelingnetworkinghomelab

You want to SSH into a machine you can’t accept connections to: a home server behind your router, a Raspberry Pi at the office, a box on a mobile or 5G link. The old answer — forward port 22 on the router — increasingly isn’t available, and where it is, it’s a bad idea. This post walks the four approaches that actually work, what each one costs you, and a worked example of each. None of them opens an inbound port on the machine you’re reaching.

Why you can’t just forward a port anymore

Two things get in the way. First, carrier-grade NAT (CGNAT): most home fibre, cable, mobile and Starlink connections now share one public IP across many customers, so there’s no port on “your” address to forward — the router you control isn’t the one holding the public IP. Second, even with a real public IP, a forwarded SSH port is found by scanners within minutes; the logs fill with brute-force attempts from the moment it goes live. The fix in every case below is the same shape: the private machine makes an outbound connection to something reachable, and you ride that connection back in. Outbound works even through CGNAT and locked-down firewalls, because it’s just another connection leaving the network.

The four options at a glance

ApproachWhat you runExtra infrastructureLong-lived key on the box?Best for
Reverse SSH tunnelssh -R + autossh on the boxa VPS with a public IPyes (to the VPS)you already rent a VPS
Mesh VPN (Tailscale / WireGuard)a VPN client on both endsa coordination serviceyour normal SSH keyreaching several machines
Cloudflare Tunnelcloudflared on the boxa Cloudflare account + domainyour normal SSH keyyou already use Cloudflare
Outbound connectora small agent on the boxa Termalin accountno — short-lived certificatebrowser access, no VPS or VPN

The first three give you a normal ssh you@… from your laptop. The last one reaches the box from a browser or an AI agent instead. Read on for which fits.

Option 1 — Reverse SSH tunnel through a VPS

If you already rent a cheap VPS with a public IP, the private box can dial out to it and forward its own SSH port back. On the box behind NAT:

ssh -N -R 2222:localhost:22 tunnel@vps.example.com

That opens port 2222 on the VPS and pipes every connection to it back to port 22 on the home box. From anywhere, jump through the VPS to land on the box:

ssh -J tunnel@vps.example.com -p 2222 you@localhost

A raw ssh -R dies the first time the network hiccups, so wrap it in autossh under a systemd unit to keep it alive across reboots and drops, and add -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes. The full mechanics — including the GatewayPorts gotcha and why the tunnel binds to the VPS’s loopback by default — are in SSH reverse tunnels, explained. Trade-off: you’re maintaining a VPS and a long-lived key on the box that can reach it, and that VPS is now part of your attack surface.

Option 2 — A mesh VPN (Tailscale or WireGuard)

Instead of exposing one port, put both machines on a private network only they can see. Tailscale is the low-effort version. On the box behind NAT:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Install Tailscale on your laptop too, and both get a stable 100.x.y.z address (and a MagicDNS name). Then it’s just:

ssh you@home-box

as if you were on the same LAN — the box is never exposed to the public internet at all. sudo tailscale up --ssh can even handle the SSH auth for you, so you don’t manage keys for it. WireGuard is the same idea built by hand: you run the coordination yourself and configure peers manually — more work, no third-party coordinator. For a home lab with a NAS and a couple of Pis, a mesh VPN is usually the whole answer, and it’s the default I reach for in the home NAS/server guide. Trade-off: every device that needs access has to join the network, which is a client to install and an account to run.

Option 3 — Cloudflare Tunnel

If your domain is already on Cloudflare, cloudflared gives you an outbound tunnel with no open port. On the box:

cloudflared tunnel login
cloudflared tunnel create home

Route a hostname to the tunnel and point it at local sshd (ssh://localhost:22) in the tunnel config, then run cloudflared as a service. On the client side, SSH reaches it through a ProxyCommand in your ~/.ssh/config:

Host ssh.example.com
    ProxyCommand cloudflared access ssh --hostname %h

Now ssh you@ssh.example.com flows through Cloudflare’s edge to the box. The free tier covers this. Trade-off: you’re routing your SSH through a third party and taking a dependency on a Cloudflare account and a domain you host there — great if you already do, overkill if you don’t.

Option 4 — An outbound connector (no VPS, no VPN)

The three options above each ask for standing infrastructure — a VPS, a VPN, or a Cloudflare domain. Termalin’s Connector collapses that to one command and reaches the box from your browser or an AI agent instead of a local ssh. On the private server, one line installs a small static agent (Linux x86_64 and arm64):

curl -fsSL https://termal.in/tunnel-install.sh | sudo TERMALIN_TOKEN=<token> sh

The agent dials out to Termalin over a single WebSocket and holds it open; it never listens on a port. When you open a terminal in the web client, the request travels down that link and the agent connects to the box’s own sshd on localhost — it only ever moves opaque SSH bytes, so it never sees your credentials or session plaintext. Enrolment makes the box’s sshd trust Termalin’s SSH certificate authority, so logins use a short-lived certificate rather than a long-lived private key sitting on the server. That’s the one row in the table with “no” under long-lived key on the box.

The catch, stated plainly: you reach the box through Termalin’s hosted side — the web terminal or an AI agent over the built-in MCP server — not with a raw ssh command from your own machine, and the Connector is part of the paid tier (or the trial). If what you want is a normal terminal from your laptop, options 1–3 fit better. If what you want is browser access from any device — or to let an agent work on a box that has no public address and no key to steal — this is the option built for it.

Which one should you pick?

  • You already rent a VPS: the reverse tunnel reuses it — one ssh -R under autossh and you’re done.
  • You have more than one machine to reach, or want them to feel like a LAN: a mesh VPN like Tailscale scales best and exposes nothing.
  • Your domain lives on Cloudflare: their tunnel is a natural fit with no new vendor.
  • You want zero standing infrastructure, browser access, or supervised agent access: an outbound connector, with no long-lived key left on the server.

All four share the one principle worth remembering: never open an inbound port on a machine you can’t afford to have scanned. Let it reach out instead — that single reversal of direction is what makes SSH behind NAT safe.


Termalin is a free, cross-platform SSH client with saved tunnels, a web terminal and a keyless Connector for private servers — download it, or see how it handles your keys safely.

Try it on one host.

Termalin is a fast SSH client for you — and your agents.

Free tier · 14-day Pro trial · pricing