← Back to Articles
Updated September 13, 2026

Remote Access to SSH Servers Using Serveo

Reach an SSH server behind NAT without opening an inbound firewall port. The target machine makes an outbound SSH connection to Serveo; your client uses Serveo as a jump host to reach it.

Your clientServeoThe target machine

Start the reverse tunnel on the target

Run this on the machine you want to reach. Replace my-long-private-alias with a long, hard-to-guess name and keep the command running:

ssh -NT -o ExitOnForwardFailure=yes -R my-long-private-alias:22:localhost:22 serveo.net

This creates a private Serveo route. It does not open port 22 directly to the public Internet, but the alias is a routing name rather than an authentication secret. Keep public-key authentication enabled on the target SSH server.

Connect from your client

On the computer you are connecting from, use Serveo as the OpenSSH jump host:

ssh -J serveo.net your-user@my-long-private-alias

The first SSH connection terminates at Serveo. Serveo then carries the second SSH connection to port 22 on the target through the existing reverse tunnel. Your target server still authenticates your-user normally.

Give the client a short name

Add this to ~/.ssh/config on the client:

Host home-via-serveo
  HostName my-long-private-alias
  User your-user
  ProxyJump serveo.net
  ServerAliveInterval 30
  ServerAliveCountMax 3

Then connect with:

ssh home-via-serveo

Keep the target tunnel running on Linux

Before automating it, configure an SSH key that works non-interactively and run the target command by hand. Then create a user service at ~/.config/systemd/user/serveo-tunnel.service:

[Unit]
Description=Serveo reverse SSH tunnel
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/ssh -NT -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -R my-long-private-alias:22:localhost:22 serveo.net
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now serveo-tunnel.service
journalctl --user -u serveo-tunnel.service -f

An abrupt network failure can leave an alias occupied briefly while the old connection is detected and removed. The keepalives above make the client reconnect promptly; if Serveo reports that the alias is already taken, let the service retry instead of starting competing copies.

Windows

Current Windows releases include an OpenSSH client, so the same target and client commands work in PowerShell. Start by following the Windows OpenSSH and key setup section. Once the command works interactively, Windows Task Scheduler can start ssh.exe at login. Keep the SSH options as separate arguments and select a restart-on-failure policy.

Troubleshooting

  • Alias already taken: another live or recently disconnected tunnel owns that alias and port. Stop duplicate clients and retry after the stale connection clears.
  • Permission denied on the second connection: the target SSH server rejected your user or key. Test ssh your-user@localhost on the target first.
  • Forwarding request failed: keep ExitOnForwardFailure=yes enabled so automation exits and retries instead of appearing healthy without a tunnel.
  • Port 22 is blocked: the target can connect to Serveo over port 443 with ssh -p 443 ... serveo.net.
  • Old OpenSSH client: use the ProxyCommand form in the documentation when -J is unavailable.

Security boundary

Serveo transports the connection; it does not replace SSH authentication on your target. Disable password login where practical, protect target keys, choose an unguessable alias, and stop the reverse tunnel when it is no longer needed. For long-lived network access across changing Wi-Fi connections, also consider Serveo's WireGuard transport.