We stand with Ukraine
Go Wombat logo

How To Use SSH Tunnel To Expose A Local Server To Internet

Article by

Updated on February 20, 2026

Read — 3 minutes

As R&D Lead at Go Wombat, I want to share how we use an SSH tunnel to expose a local server to the internet.

We have a local server at Go Wombat that cannot be reached from outside the local network. We deployed one of our internal tools on a virtual machine on that server and used a remotely hosted server to make the tool reachable over the internet.

It is a self-hosted alternative to tools like ngrok or Serveo that needs only a server you already have.

TL;DR: systemd service for the tunnel

On the local server, create a file in /etc/systemd/system/ named something like tool-tunnel.service:

[Unit]
Description=AutoSSH tunnel to remote server
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

[Install]
WantedBy=multi-user.target

On the remote server, add the local server's public SSH key to ~/.ssh/authorized_keys.

Remote server

Giving access

To open an SSH tunnel to the remote server, the local server needs SSH access to it. Generate a key with ssh-keygen (or use an existing one), then add the public key from the local server to ~/.ssh/authorized_keys on the remote server. Since OpenSSH 9.5, ssh-keygen creates an Ed25519 key by default (~/.ssh/id_ed25519); if that is your key, use its path instead of id_rsa in the commands below.

To test it, connect from the local server to the remote server over SSH (caution: possible “server” overdose): ssh username@[IP]

Local server

SSH Tunnel

Now the SSH tunnel itself. Use a command like this:

ssh -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

What each argument does:

  • -N : do not execute a remote command. Useful when you only forward ports.

  • -i /home/tool/.ssh/id_rsa : Which identity (key) to use.

  • user@[IP] : the username and IP address of the remote server

  • -R 8080:localhost:80 : lets anyone on the remote server connect to TCP port 8080 on the remote server. The connection is tunnelled back to the client host, which then makes a TCP connection to port 80 on localhost.

  • -C : Enables data compression

Now test it by opening your URL in a browser or sending a few requests. Don’t forget to launch your application 😉

AutoSSH

The tunnel also has to be persistent. If the SSH connection drops, for example after a server-side timeout, the tunnel should be re-established automatically.

AutoSSH does exactly that.

Autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic.

The command I came up with:

autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

The only changes are autossh instead of ssh and the -M 20000 argument, which sets the monitoring port. AutoSSH uses two ports for monitoring, 20000 and 20001, so both must be free. Alternatively, use -M 0 to turn monitoring off and rely on the SSH options ServerAliveInterval and ServerAliveCountMax.

Service

To bring the tunnel up automatically after a reboot and to control the process, set up a systemd service.

Create a file in /etc/systemd/system/ named something like tool-tunnel.service (change tool to something more descriptive 😉):

[Unit]
Description=AutoSSH tunnel to remote server
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

[Install]
WantedBy=multi-user.target

P.S. Don’t forget to change [IP] in the command.

Once the file is saved, run:

sudo systemctl daemon-reload
sudo systemctl start tool-tunnel  # To start your service
sudo systemctl enable tool-tunnel  # To make it start automatically

Check the service with sudo systemctl status tool-tunnel.

Done!

If you don’t want to use Nginx

By default, OpenSSH only allows connections to remote forwarded ports from the server host itself. You can change that.

Set GatewayPorts to yes in sshd_config (usually /etc/ssh/sshd_config), as shown below. This makes the forwarded port reachable from any address, so protect it with a firewall, or use GatewayPorts clientspecified to choose the bind address for each tunnel.

GatewayPorts yes

If you want to use Nginx

Remote server

First, prepare the remote server to accept requests and forward them to the port used by the SSH tunnel. We used Nginx for this.

Create a file with the config for your site in /etc/nginx/sites-available/.

The contents are pretty simple:

server {
    listen 80;
    server_name tool.gowombat.team;
    access_log /var/log/nginx/tool-proxy.log;
    error_log /var/log/nginx/tool-proxy.error.log;
client_max_body_size 32m;
location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Then enable it in /etc/nginx/sites-enabled/, preferably with a symlink:

ln -s /etc/nginx/sites-available/tool /etc/nginx/sites-enabled/

Local server

On the local server, we also use Nginx to serve the application and its static files. If your application is simple or does not need static files, skip this part.

The config file goes in the same directory as before:

server {
    listen 80;
    server_name tool.gowombat.team;
    access_log /var/log/nginx/tool.log;
    error_log /var/log/nginx/tool.error.log;

client_max_body_size 32m;

location /static/ {
        alias /home/tool/tool-backend/collected_static/;
    }

location / {
        include proxy_params;
        proxy_pass http://unix:/home/tool/tool-backend/tool_backend.sock;
    }

}

How can we help you ?

How can we help youHow can we help youHow can we help you