Guide · SSH
ProxyJump: SSH through a bastion host
Updated · 5 min read
ProxyJump makes SSH connect to an intermediate server first (the bastion, or jump host) and open the connection to the private server from there, in a single command: ssh -J user@bastion user@10.0.1.20. In ~/.ssh/config you add ProxyJump bastion to the private host. You authenticate to the final server from your Mac, so your private key is never copied to the bastion.
On this page
How it works
Your Mac ──SSH──▶ bastion.example.com ──TCP──▶ 10.0.1.20 (private network)
└──────────── SSH, encrypted end to end ────────────┘The bastion only relays traffic. Your session with the final server is encrypted end to end between your Mac and that server. You need OpenSSH 7.3 or later, which macOS has shipped for years.
1. From the command line
ssh -J ubuntu@bastion.example.com deploy@10.0.1.20If the bastion runs on another port:
ssh -J ubuntu@bastion.example.com:2222 deploy@10.0.1.20Several hops, comma-separated and in order:
ssh -J ubuntu@bastion-1.example.com,ubuntu@bastion-2.internal deploy@10.0.2.152. In ~/.ssh/config
Host bastion
HostName bastion.example.com
User ubuntu
IdentityFile ~/.ssh/id_ed25519
Host app-1
HostName 10.0.1.20
User deploy
ProxyJump bastionNow it’s just:
ssh app-1ProxyJump bastion uses the Host bastion block (the bastion’s user, port and key), so you don’t repeat anything.
A whole subnet behind the same bastion
Host 10.0.1.*
User deploy
ProxyJump bastionOlder OpenSSH on your side (before 7.3)
Host app-1
HostName 10.0.1.20
ProxyCommand ssh -W %h:%p bastion3. Files and tunnels through the bastion
Anything that runs over SSH honours the alias’s ProxyJump:
scp backup.sql app-1:/tmp/
rsync -av ./dist/ app-1:/srv/app/
sftp app-1A tunnel to a database on the private network:
ssh -N -L 5433:localhost:5432 app-1If the database lives on another machine the bastion can reach directly, you don’t even need app-1:
ssh -N -L 5433:db.internal:5432 bastion(→ SSH tunnel guide)
4. Why not ForwardAgent
Before ProxyJump, the usual move was to log into the bastion with ForwardAgent yes and ssh onward from there. That lets anyone with root on the bastion use your agent while you’re connected. ProxyJump makes it unnecessary: your key and your agent stay on your Mac.
Common errors
channel 0: open failed: administratively prohibited: open failed. The bastion hasAllowTcpForwarding noinsshd_config. ProxyJump needs forwarding allowed on the bastion; whoever runs it has to change that.Permission denied (publickey)on the final server. Your public key isn’t in that server’s~/.ssh/authorized_keys, or the user is wrong. Getting into the bastion fine doesn’t help: the second hop is a separate login.- It logs into the bastion as your Mac user. With
-J bastion.example.comand nouser@, SSH uses your local username. Put the user in-Jor in theHost bastionblock. Could not resolve hostname app-1.internal. The bastion resolves that name, not your Mac. Use a name the bastion knows, or the private IP.- Connection loops or hangs. A
Host *withProxyJump bastionalso applies to the bastion itself. Exclude it withHost * !bastion, or setProxyJump noneinHost bastion(above theHost *). Host key verification failed. It’s your first connection to that private server, or its host key changed. Check the fingerprint before accepting; for a legitimately changed key, runssh-keygen -R 10.0.1.20.
To see which hop fails: ssh -v app-1.
With Terminalia
Doing it with Terminalia
Terminalia imports the ProxyJump from your ~/.ssh/config and keeps it in the profile, so you don’t type it again. It connects through your Mac’s OpenSSH, so jumps, agents and known_hosts behave exactly as in Terminal: if ssh app-1 works, the profile works. Once you’re in, the SFTP browser, tunnels, logs and terminal all run over that one connection instead of repeating both hops for each.
Free · no account · macOS 14+ · Apple Silicon and Intel
FAQ
What’s the difference between ProxyJump and ProxyCommand?
ProxyJump (-J) is the short, modern way to hop through a bastion, available since OpenSSH 7.3. ProxyCommand runs whatever command you give it to open the connection, usually ssh -W %h:%p bastion. In the common case they do the same thing; use ProxyJump unless you need a custom command.
Do I need to copy my private key to the bastion?
No, and you shouldn’t. With ProxyJump you authenticate to both the bastion and the final server from your Mac; the bastion only relays encrypted traffic. What you do need is your public key in the authorized_keys of both the bastion and the final server.
Can I use different keys for the bastion and the server?
Yes. Set one IdentityFile in the Host bastion block and another in the private server’s block. SSH uses each key for its own hop. Add IdentitiesOnly yes so it doesn’t offer every other key in your agent and trip the too-many-authentication-failures limit.
Related guides
~/.ssh/config with examples
· 5 min
SSH tunnels on Mac: -L, -R and -D
· 6 min