A Practical Guide to SSH Tunnels: Local and Remote Port Forwarding
A Practical Guide to SSH Tunnels: Local and Remote Port Forwarding
Authored by Ivan Velichko | Updated: June 24, 2026
SSH is a prime example of "legacy" technology that remains indispensable. In many ways, mastering a few SSH techniques is more valuable for your career than chasing Cloud Native tools or AI agent frameworks that might be obsolete by next quarter.
By utilizing standard tools and often a single line of code, you can perform powerful networking feats:
- Connect to internal VPC endpoints via a public EC2 instance.
- Map a remote development VM's localhost port to your own local browser.
- Share a local server from a private home network with the public internet.
- Route a browser's debugging port to a sandboxed remote coding agent.
Despite using these daily, the exact syntax can be elusive. This guide serves as a visual and practical refresher.
🌐 The Laboratory Environment
To demonstrate these concepts, we use a playground consisting of four hosts distributed across three distinct networks.
Network Topology Table
| Host | Network Role | IP Address / Range | Connectivity |
|---|---|---|---|
| Local | Home Device | 192.168.0.0/24 | Home Public |
| Remote | Bastion/Gateway | 203.0.113.0/24 | Home Public Private VPC |
| Private | Internal Service | 172.16.0.0/24 | Private VPC only |
Visualizing the Connectivity
Quick Start: You can access the remote host via:
ssh remote or ssh 203.0.113.30
📥 Local Port Forwarding
Local port forwarding is the most common tunneling technique. It is used when a service is listening on a remote machine's localhost or a private interface that isn't exposed to the public internet.
Common Use Cases
- Database Management: Connecting to a remote MySQL, Postgres, or Redis instance using a local GUI tool.
- Private Web Apps: Accessing a dashboard that is only bound to a private network.
- Container Access: Reaching a specific container port without exposing it to the public web.
The Mechanics
The command follows this logic:
ssh -L [local_addr:]local_port:remote_addr:remote_port [user@]sshd_addr
Breakdown:
-L: Specifies Local port forwarding.local_port: The port your local machine will listen on.remote_addr:remote_port: The destination the remote server should send the traffic to.sshd_addr: The SSH server acting as the tunnel entrance.
Mathematically, the traffic flow is:
Pro Tip: To run the tunnel in the background without opening a shell, use the flags
ssh -f -N -L.
👨🔬 Lab 1: Basic Local Port Forwarding
Scenario: A web server is running on the remote host, but it is bound strictly to 127.0.0.1:80.
Step 1: Verify the failure Attempting to reach the service via the public IP fails:
curl 203.0.113.30:80 # Attempting remote.public
curl: (7) Failed to connect to 203.0.113.30 port 80: Could not connect to server
Step 2: Verify internal access From inside the remote host, the service is healthy:
curl localhost:80
# Output: Hello from the remote host (localhost-only service).
Step 3: Create the tunnel
On your local machine, map your port 8080 to the remote's 80:
ssh -f -N -L 8080:localhost:80 203.0.113.30
Step 4: Access the service Now, simply curl your own localhost:
curl localhost:8080
# Output: Hello from the remote host (localhost-only service).
Note: For more explicit configuration, you can use:
ssh -f -N -L localhost:8080:localhost:80 203.0.113.30
🏰 Local Port Forwarding via a Bastion Host
An SSH server can forward traffic to any machine it can reach, not just itself. When a server is used specifically to bridge the gap to a private network, it is called a Bastion Host or Jump Host.
In this case, remote_addr (the target) and sshd_addr (the tunnel) are different.
This is essential for accessing services like an OpenSearch cluster or a private database residing deep within a VPC.
👨🔬 Lab 2: The Bastion Scenario
Scenario: A target service exists on a private host (172.16.0.40:80) inside a VPC. The local workstation has no route to this network.
The Problem:
curl --connect-timeout 3 172.16.0.40:80 # Attempting private.vpc
curl: (28) Operation timed out
The Solution:
Use the remote bastion (203.0.113.30) to bridge the connection.
Learning Checklist
- Understand the difference between Local and Remote forwarding.
- Successfully map a remote localhost port to a local port.
- Use a Bastion host to access a private VPC.
- Master Remote Port Forwarding (Coming next).