PuTTY Local Port Forwarding: Reaching a Database Through a Bastion Host
How to configure PuTTY's local port forwarding through the Tunnels panel to reach an internal database via a bastion host — plus the plink equivalent, verification steps, and the security trade-offs.
31 Dec 2025, 22:51 UTC

The problem: the database you can't see
A common setup in production networks: your PostgreSQL server sits on a private subnet at 10.0.0.5:5432. Your laptop can't route to it. The only door in is a bastion host that accepts SSH. You could SSH in and run psql on the command line, but you want your local GUI client, migrations tool, or app to talk to the database directly.
PuTTY's local port forwarding solves this cleanly. It binds a port on your own machine — say, localhost:5433 — and pipes everything sent to it through the encrypted SSH session to the bastion, which then opens a plain TCP connection to the database. To your local tools, the database looks like it's running on your laptop.
Setting up the tunnel in the GUI
Before opening the session, configure the forward:
- Load or create your session for the bastion host as usual (hostname, port 22, your credentials or Pageant key).
- Navigate to Connection → SSH → Tunnels.
- In Source port, enter
5433. - In Destination, enter
10.0.0.5:5432. - Leave the Local radio button selected and click Add. The forward appears in the list as
L5433 10.0.0.5:5432. - Go back to the Session panel and save the session under a name — tunnel settings are stored with the session, so you won't retype them.
- Open the connection and log in. The tunnel is live as long as this session is.
One detail worth knowing: the destination address is resolved from the server's perspective. 10.0.0.5 means "the machine at 10.0.0.5 as seen by the bastion," not by your laptop. You can also use an internal DNS name the bastion can resolve.
Exact label wording varies slightly between PuTTY versions, so if a checkbox or caption looks different in your installed release, trust the layout over this text.
The same thing from a script
PuTTY ships with plink.exe, a command-line SSH client that accepts OpenSSH-style flags. The equivalent tunnel, run from a Windows command prompt or script:
plink -L 5433:10.0.0.5:5432 [contact removed] -N-N means "don't start a shell" — the connection exists only to carry the tunnel. This is handy for scheduled tasks or a shortcut that opens the tunnel without a terminal window. Replace user and the host with your own; the account needs SSH access to the bastion but no special privileges on the database.
Verifying it actually works
Don't assume — check both ends.
- On your machine, run
netstat -an | findstr 5433(orTest-NetConnection 127.0.0.1 -Port 5433in PowerShell). You should see the port listening on 127.0.0.1. - Connect with the real client:
psql -h 127.0.0.1 -p 5433 -U yourdbuser yourdb. Use 127.0.0.1 rather than "localhost" if your client tries a Unix socket by default. - If something fails, right-click the PuTTY title bar and open the Event Log. Forwarded-port open and refused-connection messages appear there and usually tell you whether the problem is local (port already in use) or remote (bastion can't reach 10.0.0.5).
The trade-offs you should actually weigh
Who else can use your tunnel. By default the forwarded port binds to loopback, so only processes on your machine can use it. The checkbox "Local ports accept connections from other hosts" changes the bind to all interfaces — every machine that can reach your laptop can now ride your tunnel into the private network. That's occasionally useful for sharing access, and a bad idea on any network you don't fully trust. Leave it off unless you have a specific reason.
Encryption stops at the bastion. The hop from your laptop to the bastion is inside SSH. The hop from the bastion to 10.0.0.5:5432 is plain TCP unless the database connection itself is encrypted (e.g., PostgreSQL with SSL enabled). On a trusted internal subnet this is often acceptable; know which situation you're in.
Idle tunnels die silently. NATs and stateful firewalls drop idle connections, and the tunnel dies with the SSH session. Under Connection, set "Seconds between keepalives" to something like 60 to keep the session alive through aggressive middleboxes.
Policy, not just technology. A tunnel that works is not a tunnel that's allowed. Port forwarding can bypass controls your organization's network team put in place deliberately; check before tunneling into production.
Closing thought
Local forwarding turns PuTTY from a terminal emulator into a precise, auditable access path: one port, one destination, alive only while you need it. Save the session, bind to loopback, verify with netstat and the Event Log, and close the window when you're done. If you find yourself needing arbitrary destinations rather than one fixed service, look at PuTTY's Dynamic option in the same Tunnels panel — it creates a local SOCKS proxy, which is a topic worth its own careful security review.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.