skip to content

SSH Tunnels & Port Forwarding

Local, remote, and dynamic SSH tunnels with practical examples.

2 min read 6 snippets 3d ago

SSH Tunnels & Port Forwarding#

Local port forwarding (-L)#

Forward a local port to a remote destination via an SSH host.

# Access remote DB on localhost:5432
ssh -L 5432:localhost:5432 user@jumphost.example.com

# Access a host only reachable from the jump host
ssh -L 8080:internal-server:80 user@jumphost.example.com

# Keep alive, no shell, background
ssh -fNL 5432:db.internal:5432 user@jumphost.example.com

Remote port forwarding (-R)#

Expose a local service on a port of the remote host.

# Make local :3000 reachable as remote :9000
ssh -R 9000:localhost:3000 user@remote.example.com

# Bind to all interfaces on remote (needs GatewayPorts yes in sshd_config)
ssh -R 0.0.0.0:9000:localhost:3000 user@remote.example.com

Dynamic (SOCKS5 proxy) (-D)#

# Start SOCKS5 proxy on local :1080, route through jump host
ssh -D 1080 -fN user@jumphost.example.com

# Then configure browser/curl to use SOCKS5 localhost:1080
curl --socks5-hostname localhost:1080 http://internal-site/

Jump hosts (-J / ProxyJump)#

# Single jump
ssh -J user@jump.example.com user@target.internal

# Multi-hop
ssh -J user@jump1,user@jump2 user@final.internal

~/.ssh/config patterns#

Host jump
  HostName jump.example.com
  User myuser
  IdentityFile ~/.ssh/id_ed25519

Host internal
  HostName target.internal
  User myuser
  ProxyJump jump

Host db-tunnel
  HostName jump.example.com
  User myuser
  LocalForward 5432 db.internal:5432
  ServerAliveInterval 60
  ExitOnForwardFailure yes

Then just: ssh -fN db-tunnel

Persistent tunnels with systemd#

[Unit]
Description=SSH tunnel to production DB
After=network.target

[Service]
User=tunnel
ExecStart=/usr/bin/ssh -NT -o ServerAliveInterval=60 \
  -o ExitOnForwardFailure=yes \
  -L 5432:db.prod.internal:5432 \
  user@jumphost.example.com
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target