Network & Connectivity

SSH Tunneling and Port Forwarding

Securely access remote services with SSH tunnels. Local, remote and dynamic forwarding explained with practical examples.

SSH tunneling allows you to route network traffic through an encrypted SSH connection. Useful for accessing databases, admin panels, and services not exposed publicly.

Tunneling Types

TypeCommandTypical Use
Local-LAccess a service on the server from your PC
Remote-RExpose a local service on the remote server
Dynamic-DSOCKS proxy to route all traffic

Local Port Forwarding (-L)

Makes a remote server port accessible on your local machine.

ssh -L [local_port]:[destination_host]:[destination_port] user@server

Example: MySQL Locally

The MySQL database is on 127.0.0.1:3306 on the server, not exposed publicly:

ssh -L 3307:127.0.0.1:3306 root@123.45.67.89

Now you can connect to MySQL from your PC on localhost:3307:

mysql -h 127.0.0.1 -P 3307 -u root -p

Example: Access an Admin Panel

# Grafana panel on server port 3000
ssh -L 8080:127.0.0.1:3000 root@123.45.67.89

# Now visit http://localhost:8080 in your browser

Persistent Tunnel in Background

ssh -L 3307:127.0.0.1:3306 -N -f root@123.45.67.89
# -N: don't execute commands (tunnel only)
# -f: go to background

Remote Port Forwarding (-R)

Exposes a port from your local machine on the remote server.

ssh -R [remote_port]:[local_host]:[local_port] user@server

Example: Expose a Development Server

Your Node.js server runs on localhost:3000: make it accessible from the VPS:

ssh -R 9000:localhost:3000 root@123.45.67.89

On the server, localhost:9000 now points to your local PC.

GatewayPorts

To make the port accessible from the internet (not just server's localhost), add GatewayPorts yes to /etc/ssh/sshd_config on the server, then restart SSH.


Dynamic Port Forwarding (-D): SOCKS Proxy

Creates a SOCKS5 proxy that routes all traffic through the server.

ssh -D 1080 -N -f root@123.45.67.89

Then configure your browser to use SOCKS5 proxy: 127.0.0.1:1080. All your browser traffic will pass through the VPS.


Persistent Tunnel with autossh

For tunnels that automatically reconnect if disconnected:

apt install autossh -y

# Persistent local MySQL tunnel
autossh -M 0 -N -f \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -L 3307:127.0.0.1:3306 \
  root@123.45.67.89

Systemd Service for Permanent Tunnel

nano /etc/systemd/system/ssh-tunnel-mysql.service
[Unit]
Description=SSH Tunnel MySQL
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/autossh -M 0 -N \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -o "ExitOnForwardFailure yes" \
  -i /root/.ssh/id_ed25519 \
  -L 3307:127.0.0.1:3306 root@123.45.67.89
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl enable --now ssh-tunnel-mysql

~/.ssh/config Configuration

Instead of repeating the command every time, save it in your SSH config:

Host my-vps
  HostName 123.45.67.89
  User root
  IdentityFile ~/.ssh/id_ed25519

  # Local MySQL tunnel
  LocalForward 3307 127.0.0.1:3306

  # Local Grafana tunnel
  LocalForward 8080 127.0.0.1:3000

Then just ssh my-vps and the tunnels open automatically.


Security

Warning

SSH tunnels bypass firewalls. Use them only to securely access services, not to expose insecure services. Always make sure AllowTcpForwarding yes is set consciously in /etc/ssh/sshd_config.

To disable tunneling for untrusted users:

# In /etc/ssh/sshd_config
AllowTcpForwarding no    # disable for everyone
# or for specific user in authorized_keys:
no-port-forwarding ssh-ed25519 AAAA... user@pc

On this page