Skip to main content

Jump Host and Proxy

In many production environments, internal servers are not directly accessible from the internet. You must route through a bastion host (also called a jump host). SCP supports this natively via -J (ProxyJump) and SSH config.

Architecture

flowchart LR
LOCAL[Your Machine] -->|SSH| BASTION[Bastion Host]
BASTION -->|SSH| TARGET[Internal Server]
LOCAL -.->|SCP data| TARGET

Your SCP data flows through the bastion host transparently — you interact as if connecting directly to the target.

ProxyJump (-J)

The -J flag is the modern, recommended way to route through a jump host.

Example: Single Jump Host

Command:

scp -J user@bastion.example.com ./deploy.tar.gz admin@internal-server:/opt/releases/

Output:

deploy.tar.gz                          100%  150MB  32.0MB/s   00:04

Example: Jump Host with Non-Standard Port

Command:

scp -J user@bastion:2222 ./config.yml admin@10.0.0.50:/etc/app/

Output:

config.yml                             100% 2048     1.5MB/s   00:00

Example: Chained Jump Hosts

Route through multiple bastion hosts in sequence:

Command:

scp -J user@jump1.example.com,user@jump2.example.com ./data.csv admin@target:/imports/

Output:

data.csv                               100%   50MB  20.0MB/s   00:02

SSH Config for Jump Hosts

Define the proxy in ~/.ssh/config so you don't need -J every time:

~/.ssh/config
Host bastion
HostName bastion.example.com
User jump-user
Port 22
IdentityFile ~/.ssh/bastion_key

Host internal-db
HostName 10.0.0.100
User admin
Port 22
IdentityFile ~/.ssh/internal_key
ProxyJump bastion

Example: Using Config Alias

Command:

scp ./backup.sql internal-db:/backups/

Output:

backup.sql                             100%  200MB  28.0MB/s   00:07

No -J needed — the config handles it automatically.

Combining with Other Flags

All standard SCP flags work with jump hosts:

Example: Jump + Recursive + Compression

Command:

scp -J user@bastion -rC ./project/ admin@internal:/opt/apps/project/

Output:

index.html                             100% 4096     1.5MB/s   00:00
app.js 100% 8192 2.0MB/s 00:00
style.css 100% 2048 1.2MB/s 00:00
package.json 100% 1024 0.8MB/s 00:00

Example: Jump + Port + Identity

Command:

scp -J admin@bastion:2222 -P 8022 -i ~/.ssh/deploy_key ./release.bin deploy@target:/opt/

Output:

release.bin                            100%  500MB  25.0MB/s   00:20

Legacy ProxyCommand

For older SSH versions without -J support, use ProxyCommand in ~/.ssh/config:

~/.ssh/config (legacy)
Host internal-legacy
HostName 10.0.0.100
User admin
ProxyCommand ssh -W %h:%p bastion

Or inline with -o:

Command:

scp -o "ProxyCommand ssh -W %h:%p bastion" ./file.txt admin@10.0.0.100:/tmp/

Output:

file.txt                               100% 4096     1.0MB/s   00:00

Common Pitfalls

IssueCauseFix
Connection timed outBastion firewall blocks your IPWhitelist your IP in bastion security group
Permission denied on targetKey not authorized on internal hostCopy key to internal server separately
Slow transfersDouble encryption overheadUse faster cipher (-c aes128-gcm@openssh.com)
-J not recognizedOld OpenSSH version (pre-7.3)Upgrade or use ProxyCommand

What's Next?

Encountering errors? Check Common Errors for solutions to frequent SCP problems.