Every time you type a long SSH command, you're doing work the SSH client is happy to do for you. The file ~/.ssh/config is a small, plain-text file that remembers your connection details — hostnames, usernames, ports, keys — so a sprawling one-liner collapses into a short alias like ssh work. This article covers the problem the file solves, how it works, and the eight directives you'll use every day.
The Problem
Consider a realistic command to reach a work server that lives on a non-standard port, uses a dedicated key, and expects a specific username:
There is nothing wrong with this command — it works. The problem is everything around it. You have to remember the exact key path, the port number, the username, and the fully qualified hostname. Miss one flag and the connection fails with a confusing error. Multiply that by ten servers, three keys, and a handful of usernames, and the mental bookkeeping becomes unmanageable.
People cope in ways that make things worse: they memorize the command wrong, they create shell aliases that only work in their own terminal and break for tools like scp and Git, or they hard-code the command into scripts that fail silently months later when a hostname changes. The SSH client already knows how to read a per-user configuration file and apply those settings automatically — you describe each host once and from then on you connect by name.
The File Itself
The per-user configuration file is ~/.ssh/config — a file literally named config with no extension, inside the hidden .ssh directory in your home folder. The file does not need to exist before you use SSH. If it is absent, the client simply falls back to its built-in defaults. You create ~/.ssh/config only when you want to override those defaults.
The conventional, conservative permission is 600 (owner read/write, nobody else), which keeps the file's contents private and satisfies every SSH client's expectations.
How SSH reads the file: first match wins
The client reads the file from top to bottom and keeps the first value it finds for each setting. This has one practical consequence: specific host blocks go at the top, general defaults at the bottom. A catch-all Host * block placed at the top of the file would shadow everything below it.
Host * block at the bottom.The Everyday Directives
Each directive below is explained the same way: what it does, what happens if you don't set it, and when you'd use it. Keywords are case-insensitive; argument values are case-sensitive.
1. Host — the block header Structural
Host begins a block. Everything indented below it, until the next Host line, applies only to hosts that match the pattern you write here. The pattern is the name you type after ssh — it's a label, not necessarily a real hostname. You can list several patterns on one line, and a single * means "all hosts" (useful for global defaults, at the bottom of the file).
2. HostName — the real address Convenience
ssh on the command line.HostName is the actual DNS name or IP address the alias resolves to. This is what makes ssh work actually reach work-server.example.com.
3. User — the login name Convenience
Sets the username to authenticate as on the remote host. Without it, SSH assumes the remote username matches your local one — often wrong on servers where you may be alice locally but deploy or ubuntu on the server.
4. Port — a non-default port Convenience
22 — the standard SSH port.Sets the TCP port to connect to. The config remembers it for ssh, but also for scp, sftp, and Git-over-SSH — sparing you the inconsistent port flags those tools use (-p versus -P).
Moving SSH off port 22 doesn't make a server meaningfully more secure — a port scan finds the service in seconds. Its real benefit is log hygiene: most automated brute-force bots only probe port 22, so relocating the service reduces failed-login noise in the logs. Real hardening comes from key-only authentication, not the port number.
5. IdentityFile — which private key to use Recommended
~/.ssh/: id_ed25519, id_rsa, and a few others.Points at the specific private key for this host. You need it when you keep more than one key and want a given host to use a specific one. Also required whenever your keys use non-standard filenames, since SSH won't discover those by default.
Important quirk: on its own, IdentityFile only adds a preferred key to the list. It doesn't stop the client from also trying every other key your agent holds — which is what the next directive fixes.
6. IdentitiesOnly — the critical companion Critical
no — meaning the client offers every key it can find, one by one, until one is accepted.Setting IdentitiesOnly yes tells the client to offer only the specific key you named with IdentityFile, and nothing else — even if your agent has other keys loaded.
Why does that matter? Servers only allow a small number of authentication attempts per connection — typically six — before hanging up. Each key your client offers counts as one attempt. If you hold seven keys, the server disconnects before the right one is even tried, and you see the infamous error:
There's also a privacy angle: offering every public key you hold to a server tells whoever runs it the full list of identities you control. Restricting the offer is both a reliability fix and an information-leak fix.
Put IdentitiesOnly yes once in a Host * block at the bottom of your file, then give each host its own IdentityFile. You get correct behaviour everywhere without repeating yourself.
7. AddKeysToAgent — load keys on first use Recommended
no — you have to run ssh-add manually to load a key into your agent.When set to yes, the SSH client loads your key into the running agent the first time it's used. You type the the passphrase once, and every subsequent connection uses the cached key silently. This removes the friction that tempts people to strip passphrases off their keys — which is much worse for security.
Other values are available: ask prompts before loading, confirm requires approval on each use, and a time interval like 1h sets an expiry. Requires a running agent — silently ignored otherwise.
8. UseKeychain — store the passphrase (macOS only) macOS
no. Also: this option only exists in Apple's version of SSH — not in mainline OpenSSH.On macOS, UseKeychain yes tells the SSH client to fetch (and store) your key's passphrase from the system Keychain. You never retype it — the key becomes usable as soon as you log into your Mac account.
Because this is an Apple-only extension, a non-Apple ssh (for example one installed via Homebrew) may reject it with an error. Guard it with IgnoreUnknown so a foreign client skips the line instead of failing.
A Practical Comparison
Meet Alice and Bob. Both have the same setup: two SSH keys on their laptop and two servers they connect to regularly. But each has written their ~/.ssh/config differently — and the consequences show up quickly.
Keys on the laptop: ~/.ssh/id_ed25519_personal and ~/.ssh/id_ed25519_work — both already loaded into the running ssh-agent. Note the non-standard filenames: SSH won't discover these on its own.
Servers: personal.example.com (default port) and work-server.example.com on port 2222.
Bob wrote something minimal that "just works" today. Alice followed the pattern this article recommends. Here are the two files side by side:
Now watch what happens when each of them runs ssh work:
What Alice did that Bob didn't
Just three things:
- Named a specific key per host with
IdentityFile. Bob names no key at all, so his client offers every identity his agent has loaded. That works today with two keys — but both get presented to both servers, the personal key is needlessly exposed, and once the number of loaded keys grows past the server's attempt limit, connections start failing. - Added
IdentitiesOnly yesin a globalHost *block. Without this, even with anIdentityFile, SSH still helpfully offers every other key it can find. Alice's config guarantees that only the named key is presented to each server. - Enabled
AddKeysToAgent yesglobally. Alice types her passphrase once per session. Bob types his every time — or, more likely, gives up and removes the passphrase entirely, which is a much worse security outcome.
Bob's config isn't broken. It works right now with his current two keys. But it's fragile: it will misbehave once his key collection grows, it leaks more information than necessary, and it fights him on convenience. Alice's config — only three extra lines and one extra block — doesn't have any of those problems.
Defaults at a Glance
| Directive | Default when unset | Purpose |
|---|---|---|
| Host | None (required) | Opens a block of settings for a named alias |
| HostName | Name typed on command line | The real address the alias points to |
| User | Your local username | Remote account to log in as |
| Port | 22 | TCP port on the server |
| IdentityFile | Standard names in ~/.ssh/ | Which private key to present |
| IdentitiesOnly | no (offers every key) | Restricts the client to the named key |
| AddKeysToAgent | no | Auto-loads keys into ssh-agent on first use |
| UseKeychain | no (macOS only) | Uses macOS Keychain to store passphrases |
A Minimal Starter Config
A small, safe configuration you can drop into ~/.ssh/config to try the alias workflow. The Host * block goes at the bottom because of the first-match-wins rule.
With that file in place, ssh work does the same thing as the long command from the beginning of the article. Every other tool that uses SSH — scp, sftp, rsync, Git — reads the same config, so scp file.txt work:/tmp/ just works too.
Main References
- ssh_config(5) — OpenBSD manual pages — man.openbsd.org/ssh_config
- ssh(1) — OpenBSD manual pages — man.openbsd.org/ssh.1
- ssh-agent(1) — OpenBSD manual pages — man.openbsd.org/ssh-agent.1
- Mozilla InfoSec — OpenSSH Guidelines — infosec.mozilla.org/guidelines/openssh
- Apple Developer Documentation — Using the macOS Keychain with SSH — developer.apple.com/library/archive/technotes/tn2449