SSH Config: 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 refuses to connect at all if anyone but you can write to
it — a curt Bad owner or permissions on ~/.ssh/config and nothing else. Merely readable it
will tolerate; writable it will not:
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 ~/.ssh/config
Section titled “A starter ~/.ssh/config”# 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.
That settles which account pushes. Which email the commits are written by is git’s business rather than SSH’s, and a conditional include in your git config sets it per directory, so work repositories stop collecting your personal address.
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.- A shell that connects fine but cannot clear its own screen, or draws
htopas abstract art, is not an SSH fault at all: yourTERMwent across and the remote host has never heard of it. Terminal colours and terminfo has the one-line fix. - Copying in Neovim or tmux on the server lands nowhere near your own clipboard, because the remote program can’t reach it. OSC 52 is the way across: the program asks your terminal to do the copying. Most terminals accept it out of the box, but GNOME Terminal and Ptyxis don’t support it and iTerm2 wants it switching on; the feature matrices have the full list for macOS and Linux.
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, and the tmux cheat sheet is the half of it you will actually have to look up. tmux rather than anything newer, for one unglamorous reason: it is already installed on the far end, which is where the argument against its rivals lives. 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.