SSH Tunneling — also known as SSH Port Forwarding — is one of the most powerful security techniques available on any Linux VPS. It lets you encrypt arbitrary TCP traffic, access internal services that are not publicly exposed, and create a SOCKS proxy through your server without installing any additional software. This guide covers every type of SSH Tunneling with real, production-ready commands.

What is SSH Tunneling and Why Does It Matter?

SSH (Secure Shell) is far more than a remote login tool. Its port forwarding capability allows any TCP connection to be wrapped inside an encrypted SSH channel and routed through your VPS. The key benefits are:

An SSH Tunnel works by establishing an encrypted channel between your local machine and the VPS, then forwarding data between a local port and a destination port through that channel. Any packet intercepted in transit is unreadable without the private key.

The Three Types of SSH Port Forwarding

SSH supports three distinct forwarding modes. Understanding when to use each one saves significant troubleshooting time.

Type Flag Direction Best used for
Local Forwarding -L local → VPS → destination Access a database or service on the VPS from your machine
Remote Forwarding -R VPS → local machine Expose a local service via the VPS public IP
Dynamic Forwarding -D local SOCKS5 → VPS → internet Use the VPS as a proxy for a browser or app

Local Port Forwarding (-L): Access VPS Services Securely

Local Port Forwarding forwards connections from a port on your local machine, through the SSH connection, to a port on the VPS (or any host reachable by the VPS). The syntax is:

ssh -L [local_port]:[destination_host]:[destination_port] user@vps-ip

The most common use case is securely connecting to a MySQL database that is bound only to localhost on the VPS — no public port 3306 exposure needed:

# Create a tunnel: local port 13306 → MySQL on VPS (localhost:3306)
ssh -L 13306:localhost:3306 [email protected] -N

# Now connect to MySQL through the local port
mysql -h 127.0.0.1 -P 13306 -u root -p

The -N flag tells SSH not to execute a remote command — just open the tunnel. Add -f to fork the process into the background:

# Background tunnel
ssh -f -N -L 13306:localhost:3306 [email protected]

The same technique applies to Redis, PostgreSQL, and any other service:

# Tunnel to Redis (port 6379)
ssh -L 16379:localhost:6379 [email protected] -N

# Tunnel to PostgreSQL (port 5432)
ssh -L 15432:localhost:5432 [email protected] -N

# Access a private admin panel running on port 8080 of the VPS
ssh -L 8888:localhost:8080 [email protected] -N
# Open http://localhost:8888 in your browser

Local forwarding can also reach other machines in the VPS's private network:

# Reach an internal server at 192.168.1.100 via the VPS
ssh -L 8080:192.168.1.100:80 [email protected] -N

Remote Port Forwarding (-R): Expose Local Services via VPS

Remote Port Forwarding opens a port on the VPS and forwards connections to your local machine. This is invaluable when you want to share a service running on your laptop or a machine behind NAT with a remote collaborator or webhook endpoint.

ssh -R [vps_port]:[local_host]:[local_port] user@vps-ip
# Open port 8080 on VPS, forwarding to local dev server on port 3000
ssh -R 8080:localhost:3000 [email protected] -N

# Anyone who accesses http://203.0.113.10:8080 reaches your local server

By default, SSH binds the remote forwarded port to localhost on the VPS, making it accessible only from the VPS itself. To allow external connections, edit /etc/ssh/sshd_config on the VPS:

# /etc/ssh/sshd_config on VPS
GatewayPorts yes
# Reload SSH daemon
sudo systemctl reload sshd

To bind to a specific interface on the VPS, include the bind address in the command:

# Bind to all interfaces on VPS
ssh -R 0.0.0.0:8080:localhost:3000 [email protected] -N

# Bind only to VPS localhost (default behavior without GatewayPorts)
ssh -R 127.0.0.1:8080:localhost:3000 [email protected] -N

Dynamic Port Forwarding (-D): SOCKS5 Proxy via VPS

Dynamic Port Forwarding creates a SOCKS5 proxy on a local port. Unlike Local Forwarding — which targets a single fixed destination — Dynamic Forwarding accepts SOCKS protocol from any application and proxies the connection to any destination reachable by the VPS.

# Create SOCKS5 proxy on local port 1080
ssh -D 1080 [email protected] -N

# Run as background process
ssh -f -N -D 1080 [email protected]

Configure your application to use the SOCKS5 proxy at 127.0.0.1:1080:

For system-wide proxying from the command line, install proxychains:

# Install proxychains
sudo apt install proxychains4

# Edit /etc/proxychains4.conf — add:
socks5 127.0.0.1 1080

# Use any command through the proxy
proxychains4 curl https://api.example.com
proxychains4 python3 myscript.py

Pro tip: Always add -o ServerAliveInterval=60 -o ServerAliveCountMax=3 to every tunnel command. This sends a keepalive packet every 60 seconds, preventing the tunnel from dropping when traffic is idle — especially important on VPS providers that aggressively cut idle TCP connections through their firewall.

SSH Config File: Managing Tunnels Efficiently

Typing long SSH commands every time is error-prone. Save your tunnel configurations in ~/.ssh/config to create easy-to-use shortcuts:

# ~/.ssh/config

# MySQL tunnel to VPS
Host vps-mysql-tunnel
    HostName 203.0.113.10
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 13306 localhost:3306
    ServerAliveInterval 60
    ServerAliveCountMax 3

# SOCKS proxy via VPS
Host vps-socks
    HostName 203.0.113.10
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    DynamicForward 1080
    ServerAliveInterval 60

# Jump Host access to internal server
Host internal-server
    HostName 192.168.1.50
    User ubuntu
    ProxyJump [email protected]
    IdentityFile ~/.ssh/id_ed25519

With this configuration in place, usage becomes trivial:

# Open MySQL tunnel
ssh -N vps-mysql-tunnel

# Start SOCKS proxy
ssh -N vps-socks

# Connect through jump host directly
ssh internal-server

autossh and systemd: Persistent Auto-Reconnecting Tunnels

For tunnels that must stay alive continuously, autossh monitors the connection and reconnects automatically when it drops.

# Install autossh
sudo apt update && sudo apt install autossh

# Run a persistent tunnel with autossh
autossh -M 0 -o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" \
    -N -L 13306:localhost:3306 [email protected]

The -M 0 flag disables autossh's own monitoring port and relies on SSH's built-in keepalive instead, which is more reliable. To make the tunnel survive reboots, wrap it in a systemd service:

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

[Service]
User=ubuntu
ExecStart=/usr/bin/autossh -M 0 \
    -o "ServerAliveInterval=30" \
    -o "ServerAliveCountMax=3" \
    -o "ExitOnForwardFailure=yes" \
    -N -L 13306:localhost:3306 \
    [email protected]
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel-mysql
sudo systemctl start ssh-tunnel-mysql

# Check status
sudo systemctl status ssh-tunnel-mysql

This service starts at boot and restarts within 10 seconds if the tunnel drops, giving you an always-available secure database connection without ever opening MySQL to the public internet.

Jump Host (Bastion Host): Accessing Private Networks

A Jump Host — or Bastion Host — uses the VPS as an intermediary to reach servers in a private network that have no public IP. This architecture dramatically reduces your attack surface by exposing SSH on only one server.

# Single command via jump host
ssh -J [email protected] [email protected]

# Using ProxyCommand (compatible with older SSH versions)
ssh -o ProxyCommand="ssh -W %h:%p [email protected]" [email protected]

In SSH config, this becomes transparent:

# ~/.ssh/config
Host bastion
    HostName 203.0.113.10
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519

Host db-server
    HostName 192.168.1.100
    User ubuntu
    ProxyJump bastion

Host app-server
    HostName 192.168.1.101
    User ubuntu
    ProxyJump bastion
# SSH handles the jump automatically
ssh db-server
ssh app-server

With this setup, no matter how many internal servers you add, all access flows through the single bastion host. This centralizes audit logging and simplifies firewall rules — a cornerstone of modern cloud security architecture.

Hardening Your SSH Server for Tunneling

Even though SSH Tunnels are encrypted, a poorly configured SSH server creates vulnerabilities. Apply these settings to /etc/ssh/sshd_config on your VPS:

# Disable password auth — keys only
PasswordAuthentication no
PubkeyAuthentication yes

# Disable direct root login
PermitRootLogin no

# Restrict which users can forward
AllowUsers ubuntu deploy

# Disable X11 if unused
X11Forwarding no

# Use a Match block to allow forwarding per user
Match User ubuntu
    AllowTcpForwarding yes
    GatewayPorts no

For a dedicated jump-host account that should only tunnel — never run shell commands:

# Tunnel-only user — no interactive shell
Match User tunnel-user
    ForceCommand /bin/false
    AllowTcpForwarding yes
    X11Forwarding no
    PermitTTY no

Additionally, restrict SSH access by IP at the firewall level:

# UFW — allow SSH only from your office IP range
sudo ufw allow from 203.0.113.0/24 to any port 22
sudo ufw deny 22

# Or move SSH to a non-standard port to avoid automated scanners
# In sshd_config: Port 2222
sudo ufw allow 2222/tcp

Frequently Asked Questions (FAQ)

What is the difference between SSH Tunneling and a VPN?

SSH Tunneling encrypts traffic only for the specific port or application you configure, making it ideal for occasional use without installing additional software. A VPN encrypts all traffic from your machine through a central server. SSH Tunneling is easier to set up if you already have SSH access to a VPS, while a VPN is better suited for system-wide routing of all internet traffic.

What is Dynamic Port Forwarding and how is it different from Local Forwarding?

Dynamic Port Forwarding creates a SOCKS5 proxy on your local machine that routes traffic through SSH to the VPS toward any destination. Local Forwarding only forwards to a single predefined host:port. Dynamic Forwarding is ideal for browsers or applications that support SOCKS proxies. Use ssh -D 1080 user@vps-ip then configure SOCKS5 proxy to localhost:1080 in your application.

How do I make an SSH Tunnel start automatically at boot?

Use autossh combined with a systemd service on Linux. Create /etc/systemd/system/ssh-tunnel.service with ExecStart=/usr/bin/autossh -M 0 -o ServerAliveInterval=30 -N -L 3306:localhost:3306 user@vps-ip. Then run systemctl enable ssh-tunnel and systemctl start ssh-tunnel so the tunnel starts at boot and reconnects automatically when the connection drops.

Is an SSH Tunnel secure enough for transmitting sensitive data?

SSH Tunnels use AES-256 encryption with key-based authentication, which is highly secure for data transmission. Best practices: disable password authentication (use SSH keys only), set AllowTcpForwarding yes for permitted users only, and restrict access by IP using firewall rules or AllowUsers in sshd_config to prevent unauthorized use of the tunnel infrastructure.

High-Performance KVM VPS by AsiaGB

AsiaGB VPS with Full Root Access — Docker, MySQL, Python, Node.js. Starting from 399 THB/month.

View VPS Plans

View all affordable VPS Thailand plans →