Most production networks put a single gateway in front of everything. A bastion, a jump host, a jump box — one machine that accepts SSH from the outside world, with the servers you actually care about sitting on a private network behind it, no public address of their own. The question is how to get through that gateway to the box behind it without making a mess of your keys. The clean answer is ProxyJump.
The bastion pattern, briefly
The reason to have a bastion is containment: only one host is exposed to the internet, so only one host is worth hardening, monitoring, and worrying about. The internal servers can’t be reached directly at all — you have to go through the gateway. That’s the whole design, and it’s a good one. It only breaks when the way people traverse the bastion quietly undoes the containment.
There are three common ways through. Two of them do exactly that.
The two wrong ways
Copying your private key to the bastion. The most tempting and the worst: scp your key up, then ssh onward from the bastion. Now your private key lives on a shared, internet-facing machine you don’t fully control — the single box most likely to be compromised. If the bastion falls, your key falls with it, which defeats the entire reason the bastion exists. Don’t put private keys on jump hosts, ever.
Forwarding your agent (ssh -A). Better, because the key stays on your laptop — but it exposes your agent socket on the bastion, where anyone with root can borrow your identity for as long as your session is open. It’s fine for a box you own outright and risky on a shared gateway, which is exactly what a bastion is. The full version of that argument is in agent forwarding is convenient and dangerous.
ProxyJump — the right way
ProxyJump does what both of those are groping toward, without the downside:
ssh -J bastion.example.com db.internal
Here’s what actually happens. Your ssh opens a connection to the bastion, then uses that connection purely as a transport to open a second SSH connection straight through to db.internal. The crucial part: authentication to db.internal happens end to end, from your laptop. The bastion only shuttles encrypted bytes back and forth — it never sees your key, your agent isn’t exposed on it, and it can’t read the inner session. It’s a tunnel, not a log-in-and-relaunch.
So you get the reach of going through the bastion, with the key never leaving your machine and nothing borrowable left behind on the gateway. That’s the whole win.
More than one hop
Real networks sometimes nest gateways. ProxyJump chains — list the hops in order, comma-separated:
ssh -J bastion.example.com,jump2.internal db.deep.internal
Each jump is tunneled through the one before it, and authentication to the final target is still end to end. You can stack as many as the topology needs.
Make it permanent in ssh_config
Typing -J every time gets old, and the point of a saved config is that the routing becomes invisible. Give the bastion its own entry, then point internal hosts at it:
Host bastion
HostName bastion.example.com
User jump
Host db
HostName 10.0.0.9
User deploy
ProxyJump bastion
Now ssh db transparently routes through the bastion — no flags to remember, and 10.0.0.9 is a private address your laptop can’t even route to directly. The bastion’s own settings (its key, a non-standard port, keepalives) live in its block and get reused for every host that jumps through it. More on structuring the file in the ssh_config guide.
ProxyJump vs the old ProxyCommand
If you’ve seen this before it was probably the older form, using ProxyCommand with netcat-style tunneling:
Host db
ProxyCommand ssh -W %h:%p bastion
ProxyJump (OpenSSH 7.3, 2016) is the clean shorthand for exactly this — -W %h:%p is the same “use this connection as a transport” trick, wrapped in one directive. ProxyCommand still earns its keep for cases -J can’t express — a cloud provider’s session-manager wrapper, a custom connector binary — but for a plain jump host, ProxyJump is what you want.
Two things to get right
- The jump host needs its own auth sorted. ProxyJump doesn’t magic away the first login — the bastion still authenticates you normally, with its own key or agent entry. A saved
Hostblock for the bastion keeps that tidy and reusable. - Host key verification happens on every hop. You’ll be asked to trust each new host the first time you reach it — the bastion and the target. That’s a feature, not a nuisance: it’s how SSH proves each server is the one you meant. Accept it once per host, deliberately, not reflexively.
And a distinction worth keeping straight: ProxyJump is not port forwarding. ProxyJump gets your shell to a host behind the bastion; port forwarding (-L) brings a service’s port from the far side to your localhost. They solve different problems and you’ll often use both — jump to the box, and forward its database port back to your laptop.
How Termalin does it
Termalin treats the jump host as part of a saved server’s config: attach a bastion to a host, and connecting to that internal box routes through the gateway automatically. The key stays on your machine — nothing is copied to the bastion, no agent socket is exposed there — and each hop’s server identity is verified on first connect, so you notice if a host ever changes underneath you. The whole host list, with its jump hosts, keys and keepalives, syncs across your machines end-to-end encrypted, so the same private box is one click away wherever you’re working.
Bottom line
The bastion exists to be the one exposed machine, and everything else exists to stay behind it. ProxyJump respects that bargain: you reach anything on the private side, and your key never leaves your laptop to get there.
Termalin is a free, cross-platform SSH client with jump-host support, built-in tunnels and a key manager — download it, or see how it handles your keys safely.