The SSH Config File: Type Less, Connect Faster
SSH has been quietly carrying developers into remote machines since 1995 — older than most of the frameworks you’ll deploy with it, and certain to outlive all of them. The tool itself is a masterpiece. The way most people use it is not:
A command with the readability of a number plate, re-Googled weekly and mistyped monthly. The
fix has been sitting in ~/.ssh/config all along: name your hosts once, and the whole
performance collapses into ssh deploy.
The file, and the one rule nobody mentions
Section titled “The file, and the one rule nobody mentions”~/.ssh/config is your personal config; /etc/ssh/ssh_config is the system-wide one, which
you can leave alone for the whole of your natural life. If the file doesn’t exist, create it —
and mind the permissions, because SSH quietly ignores anything it considers too permissive:
mkdir -p ~/.ssh && chmod 700 ~/.sshtouch ~/.ssh/config && chmod 600 ~/.ssh/configThe format is simple enough: a Host pattern, followed by the options that apply to it:
Host deploy HostName 203.0.113.10 User deployNow the rule that catches everyone: SSH uses the first value it finds for each option, not
the last. Nearly every other config format on the planet lets later lines override earlier
ones; SSH read that convention and calmly went the other way. So specific hosts go at the
top of the file, and your Host * defaults go at the bottom — the catch-all, not the
final word. If a setting ever seems to be ignored, this paragraph is why.
A starter config worth stealing
Section titled “A starter config worth stealing”# Specific hosts firstHost deploy HostName 203.0.113.10 User deploy Port 2222 IdentityFile ~/.ssh/id_ed25519
Host db.internal ProxyJump bastion.example.com User ops
# Defaults for everything else — keep this at the bottomHost * AddKeysToAgent yes UseKeychain yes # macOS only; delete on Linux ServerAliveInterval 60Host deployis the nickname; everything beneath it is what the nickname means.ssh deploy,scp deploy:app.log .andgit clone deploy:repoall work from it.HostNameaccepts DNS names as well as IPs, and a name likebox.example.comages far better than an address you’ll have to update by hand.ProxyJumphops through a bastion: connecting todb.internalsilently tunnels viabastion.example.com. The one-off flag version isssh -J bastion.example.com db.internal.ServerAliveInterval 60nudges the connection every minute, which stops NATs and hotel Wi-Fi dropping your session mid-deploy. They were going to try; now they can’t.AddKeysToAgent yesloads keys into your agent the first time you use them, so you type each passphrase once per login rather than once per connection.
Keys, if you’re still typing a password
Section titled “Keys, if you’re still typing a password”If every connection still asks for a password, fix that first — it takes two commands:
ssh-copy-id deployssh-keygen makes you a modern Ed25519 keypair: accept the default location, and set a
passphrase — you have a password manager, surely. ssh-copy-id appends the public half to
the server’s authorized_keys, and it honours the config — deploy works here just as it
does for ssh itself.
The passphrase is then the agent’s problem. With AddKeysToAgent yes — and UseKeychain yes
on macOS, which files it in the Keychain — you unlock each key once and forget about it. On
Linux, any desktop environment worth its window decorations already runs an agent; minimal
setups can start one with eval "$(ssh-agent)".
Two GitHubs, one machine
Section titled “Two GitHubs, one machine”The most common reason people meet this file: a work GitHub account and a personal one, each with its own key. The trick is two nicknames pointing at the same host:
Host github.com-work HostName github.com IdentityFile ~/.ssh/id_ed25519_work IdentitiesOnly yes
Host github.com HostName github.com IdentityFile ~/.ssh/id_ed25519_personal IdentitiesOnly yesClone work repos from [email protected]:org/repo.git and personal ones as normal; each
goes out carrying the right identity.
IdentitiesOnly yes is the load-bearing line. Without it, SSH offers every key in your
agent, one per attempt — and servers allow only a handful of attempts before losing
patience. With a well-stocked keyring, GitHub pulls the plug with Too many authentication failures before the right key comes up.
Faster reconnects: multiplexing
Section titled “Faster reconnects: multiplexing”One more trick, worth the three lines. SSH can keep a master connection open and run subsequent sessions over it:
Host * ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h-%p ControlPersist 10mmkdir -p ~/.ssh/sockets && chmod 700 ~/.ssh/socketsThe first connection to a host pays the full handshake; everything in the next ten minutes —
ssh, scp, rsync, git pull — reuses it and lands essentially instantly. On slow links
or far-away servers it feels like cheating, which is how you know it’s working.
When it misbehaves
Section titled “When it misbehaves”ssh -G deployprints the effective configuration for a host — every option, from every block, after all the matching has settled. The config file is write-many, read-never; this is how you find out what SSH actually thinks.ssh -v deploynarrates the connection attempt line by line: which config file it read, which key it offered, where it gave up.-vvand-vvvincrease the candour.
Keeping it out of git
Section titled “Keeping it out of git”As the dotfiles guide insists: ~/.ssh/ never goes into a
repository, private or otherwise. The config file itself holds no secrets, but it lives next
to all of your keys, and the simplest safe habit is keeping the whole directory out of
version control. If you want the config backed up, copy it into your dotfiles repo by hand
and leave the keys behind.
Where next
Section titled “Where next”The config gets you connected quickly; tmux keeps everything running when the connection drops anyway — between the two, that’s the whole remote-work stack. Beyond that: a terminal emulator worthy of the sessions you’re now enjoying, and the keyboard shortcuts to drive it all without touching the mouse.