SSH Local Port Forwarding

· 5min · Calvin Yong

Introduction

OpenSSH can forward a port on your client machine to a port on the remote host with the -L option. Sometimes this is also called ssh tunneling. With this you can access services that are normally restricted to the machine it's running on (localhost). In this post, I'll share a couple of cases where I have used SSH port forwarding.

Syntax

The syntax for the argument of the -L option is

-L [bind_address:]port:host:hostport

The bind_address parameter is optional. Since it is not used in this article, argument examples can simply be read as port:host:hostport. All of the examples below use localhost as the host, which refers to the remote itself that we are sshing into. It can be replaced with a different host so that it looks to the host the remote machine is connecting to it (think of it like a proxy), but the examples below are mainly to show how you can access local services on a server with SSH.

In the examples, I typically set the port the same as the hostport. You can choose to make them different if the remote port a service is running on is not configurable and you don't like the port number, or if the port is not available on your local machine, because another service is listening on it for example.

I use the -N flag in all of my examples below. The -N tells ssh not to execute any remote command. It can be omitted if you want to use the connection to run interactive commands.

Examples

VNC

To use VNC, the remote needs to run a VNC server, and the client needs a VNC client. For Xorg environments, x11vnc and TigerVNC provide VNC server implementations. For wlroots-based Wayland compositors, wayvnc provides a VNC server for those. Regardless of the remote's display server protocol, TigerVNC provides the vncviewer command for connecting to a VNC server.

On the remote:

# If the remote runs an xorg server
# -locahost is the same as "-allow 127.0.0.1" and "-listen localhost",
# which only allows localhost to connect to the server.
# -nopw disables the warning message which can be ignored
# if the SSH server and network is hardened.
x11vnc -localhost -nopw -display :0 -loop

# If the remote runs a wlroots based compositor
wayvnc -grR --max-fps=60 -L warning

On the client:

ssh -N -L 5900:localhost:5900 remote
vncviewer localhost

SPICE

SPICE servers are usually used for viewing virtual machines (but VNC can be used too). With local forwarding, you can run the virtual machine on a remote host, and connect to it remotely. Because we are using SPICE, we can also redirect USB devices remotely (when I first tested this, this felt like black magic).

On the remote virt-manager, make sure the graphics type is "SPICE", the listen type is "Address", and that the port is explicitly set. If the port is not explicitly set, and the port is already in use (the default port is 5900, the same as VNC's default port), virt-manager will automatically use the next available port. After applying the settings, start the virtual machine either with the virt-manager interface, or with libvirt with the following command:

sudo virsh start myvirtualmachine

On the client:

# Change 5902 to whatever port you want to use
ssh -N -L 5902:localhost:5902 remote
remote-viewer spice://localhost:5902

The remote-viewer command is provided by the virt-viewer package.

Jupyter

Running jupyter on a server will allow you to use the resources of the server and save some battery on your laptop. If you use CUDA for libraries like Pytorch, but your laptop doesn't have an Nvidia GPU, you can port forward Jupyter on a server with an Nvidia GPU.

On the remote:

jupyter notebook --no-browser

# Or specify a custom port with --port
#jupyter notebook --no-browser --port=8080

Jupyter will print out a link to connect to the Jupyter server. On the client:

ssh -N -L 8888:localhost:8888 remote

After running the port forwarding command, open the Jupyter link and you should see the Jupyter file browser.

Ollama

Similar to the Jupyter example, instead of running Ollama on a local laptop, you can run Ollama on a server, which will save some battery. If your server is stronger than your laptop, it'll also enable you to run bigger models and get faster responses.

Start Ollama on the remote server. On the client:

ssh -N -L 11434:localhost:11434 remote

Test the connection with

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Why is the sky blue?",
  "stream": false
}'

With this, you can run local apps that use Ollama's REST API like Continue. This can also work with Open WebUI, but just keep it installed on the same machine as Ollama, and forward Open WebUI's port instead.

Forward multiple ports

The -L flag can be specified multiple times to forward more than one port. The following example local forwards both port 5900 and 8888:

ssh -L 5900:localhost:5900 -L 8888:localhost:8888 remote

Shortening the command with SSH config file

The ssh config file ~/.ssh/config can be used to alias a long ssh command to a host name. The LocalForward keyword is the ssh config equivalent of the -L option in the ssh CLI.

Host mytunnel
    User myuser
    Hostname 192.168.1.42
    LocalForward 5900 localhost:5900

Now invoking ssh mytunnel will be as if you ran ssh -L 5900:localhost:5900 [email protected].

The next example local forwards two ports and prevents remote commands:

Host mytunnel
    User myuser
    Hostname 192.168.1.42
    LocalForward 5900 localhost:5900
    LocalForward 8888 localhost:8888
    # Does the same as -N
    SessionType none

If you want to connect to the same server, but have one host for normal ssh and the other for only local forwarding, you can split up the config like the following:

Host myserver mytunnel
    User myuser
    Hostname 192.168.1.42

Host myserver
    # Put any other options here.
    # Otherwise this host entry can be omitted

Host mytunnel
    LocalForward 5900 localhost:5900
    LocalForward 8888 localhost:8888
    SessionType none

Documentation