`ssh -L` Port Forwarding Feels Like Magic Until You Realize It Is Just a Clean Tunnel
A practical SSH local port forwarding guide for developers who need private database access, internal dashboards, or remote services available on localhost without exposing them publicly.
Why this command feels magical to newcomers: because the result looks strange at first. A remote service suddenly appears on localhost. Once the tunnel idea clicks, it becomes one of the most useful safe-access tools in development.
What ssh -L actually does
Local port forwarding creates a tunnel from a local port on your machine to a target reachable from the SSH server side. The OpenBSD ssh manual describes -L as specifying local port forwarding. That is the formal definition.
In practical terms, it means:
- you connect to an SSH-accessible machine
- SSH listens on a local port
- traffic sent there is forwarded through the tunnel to the remote target
That lets you access private services without exposing them directly to the internet.
The command pattern
ssh -L 5433:127.0.0.1:5432 user@serverThis means:
- listen locally on port
5433 - forward through the SSH connection
- reach
127.0.0.1:5432from the server side
Now your local machine can talk to the remote database through localhost:5433.
Why this is better than “just opening the port”
Because a lot of internal services were never meant to be internet-facing. Databases, admin panels, internal metrics dashboards, and staging tools are often safer when kept private.
Port forwarding lets you reach them without turning every access need into a firewall exception or public endpoint. That is the operational win: convenient access with a smaller exposure surface.
A very common real-world use case
Suppose Postgres runs on a server and only listens locally there. You want to inspect it from your laptop:
ssh -L 5433:127.0.0.1:5432 deploy@my-serverThen connect locally with:
psql -h 127.0.0.1 -p 5433Nothing mystical happened. You created a tunnel and pointed your local tool at the tunnel entry point.
Why the host part confuses people
The most common misunderstanding is the right-hand host in local_port:host:remote_port.
That host is resolved from the remote side, not from your laptop. So 127.0.0.1 there means “localhost relative to the SSH server,” not your local machine.
This is exactly why the command is so useful for reaching services bound only inside the remote environment.
A few quality-of-life options
To avoid opening an interactive shell:
ssh -N -L 5433:127.0.0.1:5432 deploy@my-server-N means do not execute a remote command. That is often what you want for a pure tunnel.
You can also use config aliases from ~/.ssh/config so the tunnel command becomes shorter and less error-prone.
When this is the right tool
Use it when you need:
- secure one-off access to a remote private service
- local tooling pointed at a remote backend
- an internal dashboard or database on your laptop without public exposure
Do not use it as a substitute for good long-term architecture when a real network path or proper service access model is needed. It is a tunnel, not a complete infrastructure strategy.
Final recommendation
If you need a private remote service to appear on localhost, learn ssh -L. Once you understand which side owns which host and port, the command stops feeling magical and starts feeling like a precise, safe workflow tool.