DEV Community

JiMeJi Jin
JiMeJi Jin

Posted on Fully Autonomous

SSH through multiple jump hosts: a practical guide for Mac, iPhone, and iPad

A private server may be reachable only through a bastion host. Add a second network boundary, and the route becomes easy to mix up: which username belongs to which machine, where does each key live, and which hop is failing?

This guide starts with an OpenSSH example on a Mac, then describes how the same connection layout maps to a mobile SSH client. All addresses, usernames, and key paths below are illustrative; replace them with your own authorized hosts.

Disclosure: I develop JTerm, an SSH client for iPhone, iPad, and Mac. The OpenSSH examples work independently of JTerm.

1. Write down the route

Suppose the connection path is:

Your device -> public bastion -> internal bastion -> app server
Enter fullscreen mode Exit fullscreen mode

Your device must reach the first bastion. Each bastion must be able to reach the next machine on its SSH port. Authentication is separate at every hop.

For a quick connection with your existing authentication setup:

ssh -J alice@bastion.example.com,bob@10.0.1.10 deploy@10.0.2.20
Enter fullscreen mode Exit fullscreen mode

The comma-separated jump hosts are visited in order. The final argument is the destination; it is not another entry in the jump list. OpenSSH documents this behavior under ssh -J.

2. Give each machine its own configuration

For repeated use, add host entries to your local ~/.ssh/config. This example assumes you already have the matching private keys locally and the corresponding public keys authorized on the appropriate servers.

Host edge-bastion
    HostName bastion.example.com
    User alice
    IdentityFile ~/.ssh/id_ed25519_edge
    IdentitiesOnly yes

Host inner-bastion
    HostName 10.0.1.10
    User bob
    IdentityFile ~/.ssh/id_ed25519_inner
    IdentitiesOnly yes

Host private-app
    HostName 10.0.2.20
    User deploy
    IdentityFile ~/.ssh/id_ed25519_app
    IdentitiesOnly yes
    ProxyJump edge-bastion,inner-bastion
Enter fullscreen mode Exit fullscreen mode

Now connect with:

ssh private-app
Enter fullscreen mode Exit fullscreen mode

Set a Port in the relevant host entry if that machine uses a nonstandard SSH port. A destination's settings do not generally apply to its jump hosts, which is why each machine has its own entry. See the ProxyJump configuration reference.

You do not need to copy private keys onto the bastions or enable agent forwarding for this arrangement. Verify unfamiliar host-key fingerprints through a trusted channel before accepting them.

3. Reuse the route for file transfers

OpenSSH SFTP can use the same destination alias:

sftp private-app
Enter fullscreen mode Exit fullscreen mode

Inside the SFTP prompt, this example downloads a readable file from the remote account's home directory:

pwd
ls
get notes.txt ./notes-copy.txt
bye
Enter fullscreen mode Exit fullscreen mode

pwd and ls inspect the remote location. get downloads to your local machine; choose a local filename that will not overwrite something you need. The destination must provide an SFTP subsystem, and your remote account needs permission to read the file. See the SFTP manual.

4. Find the failing hop

Start with the shortest connection, then extend it:

ssh -v edge-bastion
ssh -v -J edge-bastion inner-bastion
ssh -v private-app
Enter fullscreen mode Exit fullscreen mode

A timeout suggests checking reachability, routing, and firewall rules for the next hop. An authentication failure means checking the username and credential for the machine named in the diagnostic output. The -v option helps distinguish those stages.

If authentication succeeds but forwarding is rejected, ask the administrator to check the bastion's forwarding policy, including AllowTcpForwarding, PermitOpen, and any account or key restrictions. An interactive shell working on a bastion does not prove that forwarding is permitted. The relevant server options are documented in sshd_config.

5. Use the same layout on iPhone and iPad

On a touch device, keeping the route and credentials organized matters more than remembering a long command. A useful connection record contains the destination, the ordered jump hosts, and the authentication details for each machine.

JTerm supports ordered multi-hop SSH connections using saved credentials for each hop. It also includes SFTP browsing, uploads, downloads, and inline text editing, alongside saved commands and iCloud sync across Apple devices.

If that fits your workflow, you can find the app and download link on the JTerm website. I would welcome feedback on what is hardest about managing servers from a phone or tablet: setting up the route, working with files, or using the terminal itself?

Top comments (0)