SSH Proxy — Examples

SSH Proxy walkthroughs: database tunnel, exposing a dev server, SOCKS proxy, jump host, ~/.ssh/config, systemd service, Windows and a tunnel script.

Back to overview: SSH Proxy · Open the live tool: www.jpkc.com/tools/ssh-proxy/

This page shows SSH Proxy through concrete walkthroughs. Hosts, users and ports are examples — plug in your own values. How the individual fields and options work in detail is covered in the manual. Remember: the tool only builds the commands; you run them in your own terminal.

Example 1: Reach a remote database

Goal: reach a MySQL database that only listens locally on the server from your own machine.

  1. Select Local Forward.
  2. SSH Host = db-server.example.com, SSH User = deploy, SSH Port = 22.
  3. In the Port Forwards table: Local Bind 127.0.0.1, Local Port 3306, Remote Host 127.0.0.1, Remote Port 3306.
  4. Leave Background and KeepAlive on, click Generate.

Result: a Bash script (OS = Linux) with this content:

Terminal
#!/usr/bin/env bash
# Local port forwarding via SSH (-L local_bind:local_port:remote_host:remote_port)

ssh \
    -L 127.0.0.1:3306:127.0.0.1:3306 \
    -f \
    -N \
    -o ExitOnForwardFailure=yes \
    -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=3 \
    deploy@db-server.example.com

Once it's running, simply point your DB client at 127.0.0.1:3306 — as if the database were running locally. The explanation table breaks down every flag. If something already uses that port locally (your own MySQL instance, say), ExitOnForwardFailure=yes makes ssh stop with an error instead of carrying on without a tunnel; pick a free local port then, such as 13306.

Example 2: Expose your local dev server

Goal: make your locally running web server (localhost:3000) reachable from a remote server, e.g. for a webhook test.

  1. Select Remote Forward.
  2. SSH Host = public.example.com, SSH User = tunnel.
  3. Table: leave Remote Bind empty, Remote Port 8080, Local Host 127.0.0.1, Local Port 3000.
  4. Tick Public on the server (GatewayPorts) (you want the port reachable from outside).
  5. Generate.

Result: an ssh -R 0.0.0.0:8080:127.0.0.1:3000 invocation — the tool fills in 0.0.0.0 itself — plus a clear comment block reminding you that the SSH server needs GatewayPorts clientspecified in /etc/ssh/sshd_config and a reload afterwards (sudo systemctl reload ssh). Without that server setting the tunnel binds only to the server's loopback interface. A warning below the output points out that the port is now open on every interface of the server — lock it down with a firewall.

If it's enough for processes on the server itself to reach your dev server (a webhook receiver that forwards locally there, for instance), leave the switch off: you then get -R 8080:127.0.0.1:3000, and the port stays private on the server.

Example 3: A SOCKS5 proxy to browse through a server

Goal: route your browser traffic through a remote server.

  1. Select Dynamic / SOCKS.
  2. SSH Host = gateway.example.com, SSH User = me.
  3. Bind Address 127.0.0.1, SOCKS Port 1080.
  4. Also tick Compression (slow link), Generate.

Result: ssh -D 127.0.0.1:1080 -f -N -o ExitOnForwardFailure=yes -C … me@gateway.example.com, followed by the test line curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me as a comment — it should show the gateway's IP address. Then point your application at the SOCKS5 proxy 127.0.0.1:1080. The Proxy Settings generator makes that easy: Proxy Type SOCKS5, host 127.0.0.1, port 1080, section Shell env vars — the variables then point at socks5h://127.0.0.1:1080, so name resolution goes through the tunnel too. Browsers have their own setting for this: in Firefox, set network.proxy.socks_remote_dns = true, otherwise DNS queries leak around the proxy.

Example 4: Reach the internal network through a bastion host

Goal: reach an internal server that's only accessible via a jump host.

  1. Select Jump Host, Mode = Modern (-J / ProxyJump).
  2. In the Jump Chain table: Host bastion.example.com, Port 22, User jumpuser.
  3. Final Destination Host = internal.example.com, Destination User = admin.
  4. Generate.

Result (KeepAlive is pre-ticked):

Terminal
#!/usr/bin/env bash
# Multi-hop SSH via ProxyJump (-J, OpenSSH 7.3+)

ssh \
    -J jumpuser@bastion.example.com \
    -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=3 \
    admin@internal.example.com

Add an Identity File such as ~/.ssh/id_ed25519 and the command is preceded by ssh-add ~/.ssh/id_ed25519: with -J, -i only applies to the destination, so the bastion host gets the key via the SSH agent. Need a second intermediate hop? Click Add Jump and fill in the row — -J takes multiple, comma-separated hops. On very old servers (before OpenSSH 7.3) choose Legacy (ProxyCommand) instead; for multiple hops the tool then emits chained ~/.ssh/config blocks.

Example 5: Bake a recurring tunnel into ~/.ssh/config

Goal: stop typing the long command every time and let ssh myserver suffice.

  1. Select SSH Config.
  2. Host Alias = myserver, HostName = db-server.example.com, User = deploy.
  3. IdentityFile = ~/.ssh/id_ed25519.
  4. Under Port Forwards, click Add Forward (the table starts out empty) and fill in the row: Type Local (-L), Bind:Port 127.0.0.1:3306, Target:Port 127.0.0.1:3306.
  5. Generate.

Result: a ready block to append to ~/.ssh/config:

# SSH config entry — append to ~/.ssh/config (Windows: %USERPROFILE%\.ssh\config)

Host myserver
    HostName db-server.example.com
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ExitOnForwardFailure yes
    LocalForward 127.0.0.1:3306 127.0.0.1:3306

# Usage: ssh myserver   — check what ssh makes of it: ssh -G myserver

After that ssh myserver is enough, and the forward comes up automatically. Use Copy, then append the block to the end of your config. To see whether ssh reads it the way you intended, run ssh -G myserver: it prints the effective configuration for that alias without opening a connection.

Example 6: Run a tunnel as a systemd service at boot

Goal: keep a database tunnel running permanently as a service that comes back up by itself after a reboot.

  1. Select Autostart Setup, Autostart Method = systemd (Linux).
  2. SSH Host = db-server.example.com, SSH User = deploy, Identity File = ~/.ssh/id_ed25519.
  3. Tunnel Type = Local (-L), Forward Spec = 3306:127.0.0.1:3306.
  4. Service Name = db-tunnel, Run as User = your local user name, e.g. anna (leave the field empty and the service runs as root, looking for keys in /root/.ssh — the tool warns you about that).
  5. Generate.

Result: two tabs. tunnel.sh holds the start script; db-tunnel.service holds a systemd unit that waits for network-online.target, with User=anna, Restart=always and RestartSec=10, plus the install instructions baked into the comments (sudo cp …, daemon-reload, enable --now, journalctl -u db-tunnel -f). The ExecStart line calls /usr/bin/ssh with -o BatchMode=yes: the service never asks for a password or a confirmation. So log in once by hand as anna with ssh deploy@db-server.example.com to accept the host key, and use a key without a passphrase or an agent — the note below the output says the same.

Save the service tab with Download (the file name db-tunnel.service is already right), copy it to /etc/systemd/system/ and enable it — the tunnel then survives reboots and drops. If you need several tunnels managed centrally, the Management Script generator is the better fit (see example 8).

Example 7: Run the same tunnel on Windows

Goal: run the database tunnel from example 1 on a Windows machine with the built-in OpenSSH client.

  1. Select Local Forward, OS = Windows (OpenSSH).
  2. SSH Host = db-server.example.com, SSH User = deploy, Identity File = C:\Users\anna\.ssh\id_ed25519.
  3. Port forward as in example 1, leave Background and KeepAlive on, Generate.

Result: a PowerShell script. Since Windows OpenSSH has no -f, it starts ssh hidden in the background and tells you the process ID for stopping it:

Terminal
# Local port forwarding via SSH (-L local_bind:local_port:remote_host:remote_port)
# PowerShell — Windows 10/11 ships OpenSSH (ssh.exe)

# Windows OpenSSH has no -f: Start-Process runs ssh hidden in the background
$tunnel = Start-Process -WindowStyle Hidden -PassThru -FilePath ssh -ArgumentList @(
    '-L',
    '127.0.0.1:3306:127.0.0.1:3306',
    '-i',
    'C:\Users\anna\.ssh\id_ed25519',
    '-N',
    '-o',
    'ExitOnForwardFailure=yes',
    '-o',
    'ServerAliveInterval=60',
    '-o',
    'ServerAliveCountMax=3',
    'deploy@db-server.example.com'
)
Write-Host "Tunnel running, PID $($tunnel.Id) - stop with: Stop-Process -Id $($tunnel.Id)"

Download saves the script as local-forward.ps1. Tick Auto-Reconnect as well and it sticks to what Windows has on board, even with autossh selected: the tool generates a while ($true) { … } loop that restarts ssh Retry Delay seconds after every drop, and the note explains why. If the tunnel should start at every logon, use Autostart Setup with Task Scheduler (Windows) — import the XML file with schtasks /create /tn "ssh-tunnel" /xml ssh-tunnel.xml.

Example 8: Manage and monitor several tunnels with one script

Goal: start two tunnels — the database and a SOCKS proxy — with one command, check them, and restart them when they fail.

  1. Select Management Script, Script Type = Bash, Script Name = tunnels.
  2. Leave Auto-reconnect and Logging on, tick Health check.
  3. First row of the Tunnel Definitions: Name db, Type Local (-L), SSH Host db-server.example.com, User deploy, Forward 3306:127.0.0.1:3306.
  4. Add Tunnel, second row: Name socks, Type Dynamic (-D), SSH Host gateway.example.com, User me, Forward 1080.
  5. Download — the file is named tunnels.sh.

Result: a script that even runs on the older Bash 3.2 that macOS ships:

Terminal
chmod +x tunnels.sh
./tunnels.sh start          # start both tunnels
./tunnels.sh status         # [up]/[down] per tunnel
./tunnels.sh health         # do ports 3306 and 1080 accept connections locally?
./tunnels.sh restart db     # restart just one tunnel
./tunnels.sh watch          # check every 30 s and restart dead tunnels

Every tunnel runs with BatchMode=yes — so accept the host keys once by hand beforehand. Logs go to ~/.local/state/ssh-tunnels/<name>.log per tunnel; if a tunnel fails to start, start tells you after one second and points to that file. On Windows, choose PowerShell and call the script as .\tunnels.ps1 start or .\tunnels.ps1 restart -TunnelName db; there's no health there.


There's more on the individual generators in the manual, strategy and pitfalls in the tips & tricks. Get started directly in the tool.