~/.ssh/config power user features: ProxyJump, ControlMaster, IdentitiesOnly, and a starter file

Most people use ~/.ssh/config as a glorified shortcut file: Host server with a HostName and a User, save five seconds of typing. That’s the entry point, and it’s fine. But the file is a much more powerful configuration system than the average sysadmin uses, and a one-time investment in setting it up properly pays back every single day after.

Here are the features that turn it from “shortcut file” into “the thing that makes your shell environment 5–10× more pleasant to work in.”

Connection multiplexing with ControlMaster

Host *
    ControlMaster auto
    ControlPath ~/.ssh/cm/%C
    ControlPersist 600

What this does: the first ssh to a host performs the full TCP handshake + key exchange + auth. SSH then keeps that authenticated connection alive in the background for 10 minutes (ControlPersist 600). Subsequent ssh, scp, rsync, or git push commands to the same host don’t re-authenticate — they share the existing connection. On a 100 ms link, the difference is “every command takes 1500 ms” vs “the first one takes 1500 ms, every subsequent one is instant.”

Make the directory once: mkdir -p ~/.ssh/cm && chmod 700 ~/.ssh/cm. The %C placeholder hashes the host/user/port together so the socket name is unique per target. Don’t use %h or %p directly — those produce collisions on long hostnames.

Per-host settings

Host work-bastion
    HostName bastion.work.example.com
    User john.doe
    Port 2222
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes

Host work-internal
    HostName 10.20.30.40
    User john.doe
    ProxyJump work-bastion
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes

Host personal
    HostName home.duckdns.org
    User me
    Port 22
    IdentityFile ~/.ssh/id_ed25519_personal
    IdentitiesOnly yes

The non-obvious one is IdentitiesOnly yes. Without it, your SSH client offers every key in your agent to every host you connect to. If you have 5 keys loaded and the server has a tight MaxAuthTries, you can be denied auth after offering 5 wrong keys before SSH even gets to your correct one. With IdentitiesOnly yes, the client only offers the key listed in IdentityFile — fewer wasted auth attempts, fewer “too many authentication failures” errors.

ProxyJump for bastion-fronted servers

Host private
    HostName private.internal.example.com
    User deploy
    ProxyJump bastion

Your private key never lands on the bastion. The bastion is just a TCP tunnel; auth happens on your laptop both times. Combined with ControlMaster above, the second hop also gets multiplexed, so reaching the inner host is instant after the first connect.

Wildcard rules and overrides

# Defaults that apply to every host unless overridden later
Host *
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ControlMaster auto
    ControlPath ~/.ssh/cm/%C
    ControlPersist 600
    AddKeysToAgent yes
    UseKeychain yes              # macOS only — load passphrase from Keychain
    HashKnownHosts no            # readable known_hosts so I can grep it

# GitHub-specific
Host github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_github

# Internal corporate hosts: more aggressive keepalive, ProxyJump
Host *.work.internal
    User john.doe
    ProxyJump work-bastion
    IdentityFile ~/.ssh/id_ed25519_work
    ServerAliveInterval 15

Order matters: SSH walks ~/.ssh/config top to bottom and applies the first matching directive for each setting. Put specific hosts at the top, broad wildcards at the bottom. The order in the example above (specific → corporate-pattern → global default) is the canonical structure.

Match — conditional config

Match host *.dev,*.staging exec "ping -c1 -W1 vpn.example.com >/dev/null"
    ProxyJump vpn-jumphost
    IdentityFile ~/.ssh/id_ed25519_dev

Match blocks let you change config based on conditions. The example above uses exec to test if the corporate VPN’s pingable — if it is, route via the jump host; if not, fall through to other rules. Niche but useful when your network changes (laptop on home Wi-Fi vs at the office).

Includes — modular config

Include ~/.ssh/config.d/*.conf
Include ~/.ssh/config.work
Include ~/.ssh/config.personal

Split a 200-line ~/.ssh/config into work, personal, project-specific files. Useful if you want to commit your personal SSH config to dotfiles in a public repo without leaking work hostnames; put the work bits in a separate file that stays gitignored.

A few less-common ones worth knowing about

  • RemoteCommand — run a specific command upon connecting. Useful for “always start in tmux”: RemoteCommand tmux new -A -s main with RequestTTY yes.
  • SetEnv — pass an env var to the remote shell. Servers usually filter most of these via AcceptEnv on their end, but useful for things like SetEnv TERM=xterm-256color when the remote shell shows up monochrome.
  • StrictHostKeyChecking accept-new — automatically accept the host key the first time you connect to a new host (and warn if it ever changes). Safer than StrictHostKeyChecking no; avoids the every-time interactive prompt.
  • VisualHostKey yes — show ASCII-art fingerprint of the host key on connect. Lets you eyeball “yep that’s the same server” without comparing fingerprints character-by-character.
  • RequestTTY force — for servers that drop you into a non-interactive shell otherwise (rare but real on some shared bastions).

A starter ~/.ssh/config worth copying

# ~/.ssh/config — sane defaults + per-host examples

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ControlMaster auto
    ControlPath ~/.ssh/cm/%C
    ControlPersist 600
    AddKeysToAgent yes
    UseKeychain yes
    StrictHostKeyChecking accept-new
    HashKnownHosts no

Host github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_github
    IdentitiesOnly yes

Host bastion
    HostName bastion.example.com
    User you
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host private
    HostName 10.0.5.20
    User you
    ProxyJump bastion
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Drop that in, run mkdir -p ~/.ssh/cm && chmod 700 ~/.ssh/cm, customize the host blocks. The next time you ssh private, you’re going through a bastion you never have to think about, with multiplexing for instant subsequent connections, with the right key for that host, and with keepalives that survive a Wi-Fi blink. That’s a solid daily-driver setup that took five minutes to write.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.