SSH

Use an SSH config file and jump hosts

Give every server a short name in ~/.ssh/config, with its address, user, port, and key. Then reach machines behind a bastion with ProxyJump, keep idle sessions alive, and reuse one connection for many logins.

Written againstOpenSSH 9.2 (Debian 12)OpenSSH 9.6 (Ubuntu 24.04)OpenSSH 8.7 (RHEL 9)OpenSSH 9.5 for Windows
Reading time
4 min
Commands to copy
7
Last checked
24 Oct 2026

Without a config file, every connection repeats the same details: ssh -p 2222 -i ~/.ssh/id_ed25519 [email protected]. With one, it’s ssh web1, and scp, rsync, and git read the same file, so the short name works for them too.

Give a server a short name

~/.ssh/config
Host web1    HostName 192.0.2.10    User admin    Port 2222    IdentityFile ~/.ssh/id_ed25519    IdentitiesOnly yes
shell
$ chmod 600 ~/.ssh/config$ ssh web1$ rsync -av ./site/ web1:/var/www/site/
SettingWhat it does
HostThe name you type. Everything indented under it applies when that name matches.
HostNameThe real address or DNS name to connect to.
UserThe login name, instead of user@ on the command line.
PortThe server’s SSH port, 22 if left out.
IdentityFileThe private key to offer.
IdentitiesOnly yesOffers only that key, not every key in the agent. sshd drops a client after six failed keys by default (MaxAuthTries 6), which shows up as Too many authentication failures.

Order the blocks from specific to general

ssh reads the file from the top, and for each setting the first value it finds wins. A host can match several blocks: its own, a wildcard, and Host *. Put the specific blocks first and the defaults for everything at the end, or the defaults override what you set further down.

~/.ssh/config
Host web1    HostName 192.0.2.10    Port 2222 # Every machine under example.comHost *.example.com    User admin    IdentityFile ~/.ssh/id_ed25519    IdentitiesOnly yes # Defaults for every host, lastHost *    ServerAliveInterval 30    ServerAliveCountMax 3

ServerAliveInterval 30 sends a keep-alive through the encrypted connection every 30 seconds, and ServerAliveCountMax 3 gives up after three go unanswered, so after 90 seconds. That keeps a NAT router or a firewall from dropping a quiet session, and ends a dead one instead of leaving the terminal frozen.

Reach servers behind a jump host

When the servers you manage aren’t reachable from the internet, you connect to a bastion first and go on from there. ProxyJump (OpenSSH 7.3 and later) does both in one command. The connection to the inner server is encrypted end to end through the bastion, and your private key stays on your machine, so the bastion doesn’t need a copy of it.

shell
# once, on the command line$ ssh -J [email protected] [email protected]
~/.ssh/config
Host bastion    HostName bastion.example.com    User admin Host db1 db2    HostName %h.internal.example.com    User admin    ProxyJump bastion

%h stands for the name you typed, so ssh db1 connects to db1.internal.example.com through the bastion. Several jumps go in one line, separated by commas: ProxyJump bastion,gateway2.

Reuse one connection for many logins

Opening an SSH connection takes a key exchange and an authentication, which adds up when a script runs 50 commands or an editor opens a session per file. A control master keeps the first connection open and runs later sessions to the same host through it, without logging in again.

~/.ssh/config
Host *    ControlMaster auto    ControlPath ~/.ssh/cm-%C    ControlPersist 10m
SettingWhat it does
ControlMaster autoUses an open master connection if there is one, and makes this connection the master if there isn’t.
ControlPath ~/.ssh/cm-%CWhere the socket goes. %C is a hash of the local host, remote host, port, and user, so the path stays short and unique.
ControlPersist 10mKeeps the master open for 10 minutes after the last session closes.
shell
$ ssh -O check web1Master running (pid=48210)$ ssh -O exit web1Exit request sent.

ssh -O exit closes the master, which you need after changing a host’s settings, since sessions through an open master keep the old ones. The OpenSSH that ships with Windows doesn’t support control masters, so leave these lines out of a config file you use there.

Split the file with Include

One file per customer or project keeps a long config readable. Include reads other files at the place where it stands, and a relative path is relative to ~/.ssh. Put it at the top of the file, before the first Host line: inside a block, it only applies when that block matches.

~/.ssh/config
Include config.d/* Host *    ServerAliveInterval 30

See what ssh will use

ssh -G prints the settings ssh would use for a host after reading every block, without connecting. Check it when a host doesn’t behave the way the file says it should.

shell
$ ssh -G db1 | grep -E '^(user|hostname|port|proxyjump) 'user adminhostname db1.internal.example.comport 22proxyjump bastion

To set up the key these examples use, see Switch a server to SSH key login.

Last checked on 24 October 2026 against OpenSSH 9.2 (Debian 12), OpenSSH 9.6 (Ubuntu 24.04), OpenSSH 8.7 (RHEL 9), and OpenSSH 9.5 for Windows. If a step doesn’t match what you see, contact us with the command and what it printed.