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
Host web1 HostName 192.0.2.10 User admin Port 2222 IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes
$ chmod 600 ~/.ssh/config$ ssh web1$ rsync -av ./site/ web1:/var/www/site/
| Setting | What it does |
|---|---|
Host | The name you type. Everything indented under it applies when that name matches. |
HostName | The real address or DNS name to connect to. |
User | The login name, instead of user@ on the command line. |
Port | The server’s SSH port, 22 if left out. |
IdentityFile | The private key to offer. |
IdentitiesOnly yes | Offers 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.
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.
# once, on the command line$ ssh -J [email protected] [email protected]
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.
Host * ControlMaster auto ControlPath ~/.ssh/cm-%C ControlPersist 10m
| Setting | What it does |
|---|---|
ControlMaster auto | Uses an open master connection if there is one, and makes this connection the master if there isn’t. |
ControlPath ~/.ssh/cm-%C | Where the socket goes. %C is a hash of the local host, remote host, port, and user, so the path stays short and unique. |
ControlPersist 10m | Keeps the master open for 10 minutes after the last session closes. |
$ 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.
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.
$ 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.