ssh -J / ProxyJump: Reach a Host Through a Bastion
ssh -J jump connects to the final host by tunneling through one or more intermediate bastion hosts.
Production hosts often sit behind a bastion. ProxyJump reaches them in one command without agent forwarding, which makes it the safer choice for multi-hop deploys.
What it does
ssh -J (ProxyJump) establishes a connection to each jump host in turn and tunnels the final connection through them. Authentication to the target happens end to end from the runner, so the bastion never sees the target session's plaintext.
Common usage
ssh -J bastion.example.com user@internal-host
# multiple jumps, comma-separated, with a specific user/port on the jump
ssh -J jumpuser@bastion:2222 user@10.0.0.5 "deploy.sh"
# the config equivalent
# Host internal
# HostName 10.0.0.5
# ProxyJump jumpuser@bastionOptions
| Form | What it does |
|---|---|
| -J <host> | Jump through one bastion |
| -J <h1>,<h2> | Chain multiple jump hosts in order |
| -J user@host:port | Set user and port on the jump host |
| ProxyJump (config) | Same behavior declared per host |
In CI
Seed known_hosts for both the bastion and the target with ssh-keyscan, since strict checking applies to every hop. ProxyJump is preferable to agent forwarding (-A) because the bastion cannot use your keys for anything else. Each hop authenticates independently, so authorize the runner key on both.
Common errors in CI
Host key verification failed. can come from either hop; keyscan and add both hosts. "Permission denied (publickey)" on the target means the key is not authorized there even if the bastion accepted it. "channel 0: open failed: administratively prohibited" means the bastion forbids forwarding to that destination; check its sshd AllowTcpForwarding and any match rules.