SSH Tunnel Dashboard Access for Your AI Agent Server — A Setup Guide

Square

You’ve got an AI agent running on some Linux box. Could be a mini PC under your desk, could be an old laptop in the closet. It’s doing its thing. Code reviews. Emails. Whatever you’ve trained it on. Problem is, it’s sitting behind NAT and you’re 300 kilometers away on a flaky coffee shop WiFi. You need to see its dashboard.

And you need it right now.

This is the exact scenario that pushed me into SSH tunnels a couple years back. I’d just set up my first autonomous agent on a repurposed NUC — Intel N100, 16GB RAM, nothing fancy — and I wanted to check its web dashboard from my phone while traveling. The machine had no public IP. The router wasn’t mine to configure. Classic NAT nightmare.

The thing is, you don’t need a VPN. You don’t need ngrok or Cloudflare Tunnels or any third-party service that’s gonna phone home with your traffic data. SSH already does this. It’s been doing it for twenty years. And it’s bulletproof if you wire it up right.

How Reverse Tunnels Actually Work

Normal SSH: you connect to a server. Reverse SSH: your private machine connects out to a public jump host and says “hey, open a port on your side and pipe everything back through me.”

That’s it. That’s the whole trick.

So your AI agent server — let’s call it agentbox — reaches out to your VPS. It opens an SSH connection like it normally would. But instead of logging into a shell, it tells the VPS: “Listen on port 8888. Anything that arrives? Forward it back through this connection to my local port 3000.”

Port 3000 on agentbox is your agent dashboard. Port 8888 on the VPS is your backdoor. When you hit vps-ip:8888 from your laptop, the traffic flows: laptop → VPS:8888 → SSH tunnel → agentbox:3000.

And honestly? It’s way simpler than people make it sound. I’ve watched three different colleagues overthink this into absurdity — setting up WireGuard bridges and containerized bastions — when a single SSH one-liner would’ve solved it.

The Dead-Simple Manual Tunnel

Start here. Don’t automate anything until you’ve seen this work once. From agentbox:

ssh -N -R 8888:localhost:3000 [email protected]

That’s the tunnel. -N means “no shell, just forwarding.” -R is the reverse flag. 8888:localhost:3000 means “open port 8888 on the remote side and send everything to my localhost:3000.”

Run it. If you get no output, it worked. SSH is silent when it’s happy.

Now from your laptop, browse to http://your-vps.com:8888 and your agent dashboard should appear. Like magic. Twenty-year-old magic.

But wait. Did it not work? Check this first: on the VPS, run ss -lntp | grep 8888. You should see SSH listening on that port. If you don’t, the tunnel failed. Common reasons: the VPS’s sshd has AllowTcpForwarding no (it should be yes), or the port’s already in use, or the VPS firewall is blocking it.

Also — and this one got me for an embarrassingly long time — reverse tunnels bind to localhost by default. If you’re trying to access the VPS port from your laptop (which is, you know, a different machine), you either need GatewayPorts yes on the VPS or you need to hop through the VPS first.

I recommend the hop. It’s safer. More on that in a minute.

Security: Lock This Thing Down

A reverse tunnel is a hole through your firewall. Treat it like one.

First, create a dedicated user on the VPS that exists only for tunneling. Don’t use your admin account. The key from agentbox should be restricted to nothing but port forwarding — no shell, no SFTP, no X11 forwarding, none of that. Here’s what a properly locked-down authorized_keys entry looks like:

restrict,port-forwarding,permitlisten="127.0.0.1:8888",command="/usr/sbin/nologin",no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3... agentbox-tunnel-key

That key can open tunnels. That’s it. Even if someone steals it, they can’t get a shell.

Use Ed25519 keys, by the way. Not RSA. They’re faster, shorter, and modern SSH defaults to them anyway. Generate one with ssh-keygen -t ed25519 -a 64 -f tunnel_key.

On the VPS side, make sure /etc/ssh/sshd_config has:

GatewayPorts no
AllowTcpForwarding yes
ClientAliveInterval 60
ClientAliveCountMax 3

The keepalive settings matter more than you’d think. Without them, a dead NAT mapping looks like a live connection to SSH. Your tunnel appears up but drops every packet silently. I’ve debugged that exact scenario at 2 AM. Not fun.

Making It Survive Reboots with autossh + systemd

Manual tunnels die when the SSH process dies. Network blips. Server reboots. Your ISP deciding to rotate your public IP at 3 AM for no reason. You need this thing to come back automatically.

autossh is the standard answer. It wraps SSH and monitors the connection. If it drops, autossh restarts it. Install it: apt install autossh or dnf install autossh.

Then wrap it in a systemd service. Here’s the unit file I use on all my agent boxes:

[Unit]
Description=Reverse SSH tunnel to VPS for agent dashboard
After=network-online.target
Wants=network-online.target

[Service]
User=tunnelbot
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o "ExitOnForwardFailure yes" -N -R 8888:localhost:3000 [email protected] -i /home/tunnelbot/.ssh/tunnel_key
Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target

Couple things worth pointing out. -M 0 disables autossh’s built-in monitoring port — you don’t need it when you’ve got ServerAliveInterval doing the same thing at the SSH level. ExitOnForwardFailure yes makes autossh give up immediately if the port’s already taken, rather than sitting there pretending everything’s fine. And RestartSec=30 gives a reasonable cooldown between retries so you don’t hammer the VPS if something’s genuinely broken.

Drop this in /etc/systemd/system/reverse-tunnel.service, run systemctl daemon-reload, then systemctl enable --now reverse-tunnel. Your tunnel comes back after every reboot. You can check its status with systemctl status reverse-tunnel and see recent logs with journalctl -u reverse-tunnel -f.

The Dashboard URL You’ll Actually Use

So the tunnel’s up. Your agent dashboard lives on the VPS at port 8888. Not super convenient to type that in every time.

Two clean approaches. Approach A: SSH to the VPS first, then browse or curl from there. Safe because the tunnel only binds to localhost. Approach B: use a ProxyJump in your local SSH config. Way cleaner in daily use.

Put this in your ~/.ssh/config on your laptop:

Host agentbox-via-vps
  HostName 127.0.0.1
  Port 2222
  User yourusername
  ProxyJump your-vps.com
  ServerAliveInterval 30

Now you can just ssh agentbox-via-vps — one command. For the web dashboard specifically, set up a local port forward through the VPS: ssh -L 3000:127.0.0.1:8888 your-vps.com -N. Then hit http://localhost:3000 in your browser. Smooth.

But here’s a trick I actually use all the time and almost nobody writes about: set an SSH alias with a dynamic local forward. Like this: ssh -D 9999 -N your-vps.com. That gives you a SOCKS proxy on localhost:9999. Configure your browser to use that proxy. Now you can access http://127.0.0.1:8888 (your tunneled dashboard) directly in your browser. No extra port forwarding per service. One proxy, everything accessible. This is especially nice when your agent exposes multiple web UIs — the main dashboard, a monitoring page, maybe a separate logs viewer.

One proxy. All services. Beautiful.

Real World: My Setup Right Now

I’m running an AI agent on a Dell Optiplex 3060 Micro I picked up for €130 on eBay. It sits behind a CGNAT connection — no public IP, no port forwarding possible. The VPS is a €5/month Debian box at HostMyCode. The systemd unit I pasted above is running on the Optiplex right this moment, keeping the dashboard reachable.

I’ve had this setup running for about eight months. Total downtime: maybe 40 minutes across the entire period, and every single outage was either a VPS kernel update that required a manual reboot confirm, or my home ISP doing its quarterly “let’s mess with the routing tables at 4 AM” thing.

The autossh watchdog caught every network blip. The systemd unit caught every reboot. I didn’t lift a finger for 99.7% of those eight months.

And that’s really the whole point. Your AI agent should just work. You shouldn’t be babysitting tunnels. Set it up once, test it, walk away.

If you’re reading this and thinking “this seems like a lot of config for a tunnel” — I get it. But once it’s running? You forget it exists. Until you’re at a conference in another country and you casually pull up your agent dashboard on your phone and the person next to you asks “wait, how are you doing that?”

That’s when you get to look smart.

Leave a Reply

Your email address will not be published. Required fields are marked *