SecurityWritten by GLWS10 min readUbuntu 24.04 LTS / AlmaLinux 9
Commands run on your server unless a section says otherwise. Replace example usernames, addresses and paths with your own values. Keep a working session open when changing remote access.

The idea

Many tools should never be open to the whole internet: databases, Docker dashboards, monitoring pages, admin consoles. Bind them to 127.0.0.1 on the server, and use SSH to forward a port from your computer to that private address. All traffic runs through your encrypted SSH connection, and the service stays invisible to scanners.

This needs nothing extra on the server beyond SSH access, because TCP forwarding is allowed by default.

On the server: confirm the service listens only on localhost
sudo ss -lntp | grep -E "127.0.0.1:(3000|3001|5432|3306)"
The browser connects to port 8080 on your own computer. SSH forwards that connection through an encrypted tunnel to port 3000 on the VPS loopback interface. The application port stays private.
The browser connects to port 8080 on your own computer. SSH forwards that connection through an encrypted tunnel to port 3000 on the VPS loopback interface. The application port stays private.

Forward a local port to a service

The -L option binds a port on your computer and forwards it to an address as the server sees it. This example forwards local port 8080 to port 3000 on the server's own loopback interface. While the command runs, open http://localhost:8080 in your browser. Press Control-C to close the tunnel.

On your computer
ssh -L 8080:127.0.0.1:3000 deploy@YOUR_SERVER
Tunnel only, without opening a shell
ssh -N -L 8080:127.0.0.1:3000 deploy@YOUR_SERVER
The address after the first colon is looked up on the server, so 127.0.0.1 means the server itself. The local port can be any free port above 1024.

Use it with a database tool

A database client on your computer can connect to the forwarded port as if the database were local. Use a local port different from your own database, if you run one, to avoid a clash. Connect to 127.0.0.1 and that port.

On your computer: PostgreSQL
ssh -N -L 15432:127.0.0.1:5432 deploy@YOUR_SERVER
# in another terminal:
psql -h 127.0.0.1 -p 15432 -U appuser appdb
On your computer: MariaDB
ssh -N -L 13306:127.0.0.1:3306 deploy@YOUR_SERVER
mysql -h 127.0.0.1 -P 13306 -u appuser -p appdb

Keep it in your SSH config

Put the settings in ~/.ssh/config so you only type a short name. You can list several forwards for one host. The ExitOnForwardFailure option makes SSH stop with an error instead of silently running without the tunnel if a port is already in use.

On your computer: ~/.ssh/config
Host myserver-tools
    HostName YOUR_SERVER
    User deploy
    LocalForward 8080 127.0.0.1:3000
    LocalForward 15432 127.0.0.1:5432
    ExitOnForwardFailure yes
    ServerAliveInterval 30
Open it
ssh -N myserver-tools

Run it in the background

The -f option sends SSH to the background after authentication. Combine it with -N so it does not start a shell. Find and stop the tunnel afterwards with the process list. For a tunnel that must stay up and reconnect, use autossh or a systemd service, but for occasional use a foreground command is simpler and clearer.

On your computer
ssh -f -N -L 8080:127.0.0.1:3000 deploy@YOUR_SERVER
pgrep -af "ssh -f -N"
# stop it when finished:
pkill -f "ssh -f -N -L 8080"

Reach a service on a third machine

You can forward through the server to another private machine, such as a database on a private network, by naming that machine's address instead of 127.0.0.1. Use a jump host with -J when you must log in to a private server through a public one.

On your computer
ssh -N -L 15432:10.0.0.5:5432 deploy@YOUR_SERVER
ssh -J deploy@YOUR_SERVER [email protected]

Use the server as a SOCKS proxy

A dynamic forward with -D turns the SSH connection into a SOCKS proxy that sends your browser traffic through the server. It is handy for testing a site from the server's location or using a private admin page by name. Point your browser or application at the SOCKS5 address 127.0.0.1:1080.

On your computer
ssh -N -D 1080 deploy@YOUR_SERVER
curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me

Security notes

Forwards bind to your own computer's loopback address by default, so other people on your network cannot use them. Do not add a bind address of 0.0.0.0 unless you want to share the tunnel. A tunnel gives you the same network access as the server, so do not forward ports to untrusted hosts. If a tunnel will not open, check that the service listens on the server, that the local port is free and that the SSH server allows forwarding.

On your computer: is the local port free?
ss -ltn | grep 8080
On the server: check forwarding is not disabled
sudo sshd -T | grep -i allowtcpforwarding

Official documentation

These upstream references provide more detail on the commands and workflows in this guide.