SSH Port Forwarding as a Controlled Bridge for Internal Services
Use SSH port forwarding to reach internal services without exposing them. Local, remote and dynamic forwarding options, with a concrete database tunnel example and verification steps.
05 Sept 2026, 13:05 UTC

You need to reach an internal API or database that lives behind a firewall and has no public address. Opening a firewall rule is a permanent exposure. SSH port forwarding lets you reach that service through an existing SSH connection without publishing it.
The useful takeaway is that the SSH server becomes a secure relay: traffic is encrypted end-to-end between your machine and the server, and only the server can reach the internal host.
Local forwarding to reach a private service
Local port forwarding with -L maps a port on your client to a destination reachable from the SSH server. The client listens locally, forwards through the encrypted channel, and the server connects onward.
Run on the client machine as a user with SSH access to the jump host. Syntax: ssh -L [local_port:remote_host:remote_port] user@ssh-server. The remote_host is resolved by the SSH server, not your laptop.
Worked example: private database via jump host
Internal database at 10.0.0.50:5432, reachable only from jump.example.com. Create a local listener on 127.0.0.1:5433:
ssh -N -L 127.0.0.1:5433:10.0.0.50:5432 [contact removed]
-N prevents an interactive shell, keeping the connection dedicated to forwarding.
Check the result on the client with ss -lt | grep 5433 or netstat -ant. A LISTEN state on 127.0.0.1:5433 indicates the client is ready. Point your local database client at localhost:5433. Risk: binding to 0.0.0.0 exposes the tunnel to other hosts on your network; use 127.0.0.1 unless intentional.
Server side must allow forwarding. On the SSH server, /etc/ssh/sshd_config should have AllowPortForwarding yes. A restrictive setting or a per-match block will cause the client to error with "channel open failed".
Remote forwarding and dynamic proxy options
Remote port forwarding with -R maps a port on the SSH server back to a service on your client. Useful for sharing a local dev server with a colleague via the server.
Example: ssh -R 8020:127.0.0.1:3000 user@ssh-server. The server listens on 127.0.0.1:8020 and forwards to your local 3000. For accessibility beyond localhost on the server, GatewayPorts must be yes in sshd_config, which widens exposure and should be reviewed.
Dynamic forwarding with -D creates a SOCKS proxy. ssh -D 1080 user@ssh-server makes localhost:1080 a SOCKS5 listener. Configure an application to use the SOCKS proxy to route multiple destinations through the server without per-port mappings.
Trade-offs and limits
SSH tunnels are TCP only. UDP services like DNS cannot be forwarded natively without additional encapsulation.
TCP-over-TCP can degrade performance on lossy or high-latency links because congestion control runs on both the tunnel and the inner connection.
Tunnels can bypass organizational monitoring if not governed. Administrators can restrict forwarding with AllowPortForwarding no, or per-user Match blocks in sshd_config.
Verify a local tunnel by attempting a connection to localhost:local_port and confirming traffic reaches the intended remote host from the server side logs. Close the SSH process to tear down the listener.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.