May 31, 2026 · ssh · rdp · vnc · bastion · remote-desktop
How to RDP or VNC into a Machine Behind a Bastion
Need remote desktop access to a Windows or VNC machine on a private network? Here's how to tunnel RDP and VNC through an SSH jump host, plus the gotchas to watch for.
By ShellYard
You need a remote desktop on a machine that lives on a private subnet — a Windows server on RDP, or a box running a VNC server — and it's behind a bastion. You can SSH to the jump host, but your RDP or VNC client can't reach the target directly. The fix is to tunnel the desktop protocol through SSH.
Why the direct connection fails
RDP (port 3389) and VNC (port 5900 and up) are only listening on the private network. Your laptop has no route to the target and probably can't resolve its hostname — only the bastion can. So the desktop session has to travel through an SSH tunnel to the bastion, which can reach the machine.
Tunneling RDP through SSH
Forward the local RDP port through the bastion:
ssh -L 3389:windows-host:3389 user@bastion
Then point your RDP client — mstsc on Windows, Microsoft Remote Desktop on macOS, or Remmina on Linux — at localhost:3389 and connect as usual.
One gotcha: if your own machine already uses 3389, or you're tunneling to more than one Windows host, map to a distinct local port:
ssh -L 13389:windows-host:3389 user@bastion
# then connect your RDP client to localhost:13389
Tunneling VNC through SSH
Same idea on VNC's port:
ssh -L 5900:vnc-host:5900 user@bastion
# then connect your VNC client to localhost:5900
There's a real bonus here: VNC traffic is frequently unencrypted on the wire. Tunneling it through SSH doesn't just give you reachability — it wraps the whole session in SSH's encryption, which is a genuine security upgrade over connecting to a bare VNC port.
When the machine is two hops in
If the desktop target sits behind a second bastion, combine ProxyJump with the forward so the tunnel runs the whole chain:
ssh -J user@bastion1,user@bastion2 -L 3389:windows-host:3389 user@inner-host
(See SSH through multiple jump hosts for the chaining details.)
The friction
The tunnel only lives as long as the SSH session, so a dropped connection drops your desktop. If you manage a fleet of Windows or VNC machines, you're standing up and tracking a separate forward for each one, on its own local port — and doing it outside whatever remote-desktop client you actually work in.
Skipping the manual tunnel
The approach I use now keeps the desktop sessions in the same place as everything else. In ShellYard, RDP and VNC sessions route through an SSH session you already have open, in the same workspace as your shells, API requests, and databases — so there's no per-machine -L to manage and no second app juggling its own tunnel to the same jump host. It's free, local, and account-free: download it here.
For the request and database versions of this same problem, see testing an internal API behind a bastion and connecting to a database behind a bastion. The foundations are covered in SSH port forwarding explained.