termal.in

← Blog

The ~/.ssh/config file: stop retyping the same SSH flags

· Termalin team sshssh-configproductivitytutorial

If you’ve ever typed ssh -i ~/.ssh/work_ed25519 -p 2222 -o ServerAliveInterval=30 [email protected] more than twice, you’ve already earned back the ten minutes it takes to learn ssh_config. It’s the single highest-leverage file in your SSH setup: it turns that whole line into ssh web1, and it’s read automatically by ssh, scp, sftp, rsync, git, and anything else that shells out to SSH.

Here’s the practical tour — the directives that actually earn their place, and the traps worth knowing.

Where it lives and how it’s read

Your personal config is ~/.ssh/config (create it if it doesn’t exist; chmod 600). There’s also a system-wide /etc/ssh/ssh_config. The rules that trip people up:

  • First match wins, not most specific. SSH reads top to bottom and the first value it sees for each option is the one it uses. Put your specific hosts at the top and broad Host * defaults at the bottom.
  • Every setting lives under a Host block (a pattern), indented by convention (indentation isn’t required, but read the file your future self will thank you for).

The building block: a host alias

Host web1
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/work_ed25519

Now ssh web1 expands to the full command. So does scp file.tar web1:/srv/, sftp web1, and rsync -a ./dist/ web1:/var/www/. The alias is the whole point: name the server once, use the name everywhere.

Directives that earn their place

ProxyJump — reach servers through a bastion. The modern replacement for the old ProxyCommand ... netcat incantation:

Host db1
    HostName 10.0.0.5          # private address, only reachable from the bastion
    User deploy
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User jump

ssh db1 now transparently hops through bastion to reach a host with no public address. Chain them with commas (ProxyJump a,b,c) for deeper networks.

Keepalives — stop idle sessions from dying. The fix for client_loop: send disconnect: Broken pipe when you look away:

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 4

A packet every 30 seconds keeps NAT routers and firewalls from forgetting your connection; four missed replies (two minutes) before it gives up.

IdentitiesOnly — offer only the right key. If you carry several keys, SSH offers them all and a strict server replies Too many authentication failures. Pin one key per host and stop the guessing:

Host github.com
    IdentityFile ~/.ssh/personal_ed25519
    IdentitiesOnly yes

AddKeysToAgent — unlock a passphrase-protected key once:

Host *
    AddKeysToAgent yes

Patterns and wildcards — configure a fleet in one block:

Host *.staging.example.com
    User deploy
    IdentityFile ~/.ssh/staging_ed25519

Host 10.0.*
    ProxyJump bastion

A realistic config, top to bottom

# Specific hosts first (first match wins)
Host web1
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/work_ed25519

Host db1
    HostName 10.0.0.5
    User deploy
    ProxyJump bastion

Host bastion
    HostName bastion.example.com
    User jump
    IdentityFile ~/.ssh/work_ed25519

Host github.com
    IdentityFile ~/.ssh/personal_ed25519
    IdentitiesOnly yes

# Broad defaults last
Host *
    ServerAliveInterval 30
    ServerAliveCountMax 4
    AddKeysToAgent yes

Split it up as it grows: Include ~/.ssh/config.d/* at the top of the file lets you keep one file per project or client.

Two traps

  • “First match wins” bites you when a Host * block near the top sets a value you meant to override below — it won’t override, because the earlier value already won. Defaults go at the bottom.
  • Comments are # at line start. Trailing comments after a value can be parsed as part of the value on some options — keep comments on their own lines.

When the config file isn’t enough

ssh_config is excellent and everyone should have one. Its edges show when the setup gets bigger than a text file: you’re syncing it across machines by hand, sharing sanitized versions with teammates, remembering which passphrase goes with which key, or trying to see at a glance which of forty hosts is even online.

That’s the seam Termalin fills. It keeps the same per-host settings — hostname, user, port, key, jump host, keepalives — but attached to the host as structured data you organize into folders and tags, unlocks your keys once through a built-in agent, verifies each server on first connect, and shows live status for every host in the list. It imports your existing PuTTY sessions, and its host list syncs across your machines end-to-end encrypted. The file scales to a point; a client that keeps the state for you scales past it.

Either way, the principle holds: name a server once, and never retype its flags again.


Termalin is a free, cross-platform SSH client that manages per-host settings, keys and jump hosts for you — download it, or read how it handles keys safely.

Try it on one host.

Termalin is a fast SSH client for you — and your agents.

Free tier · 14-day Pro trial · pricing