I wrote a post back in 2017 about updating SSH to version 7.5p1. Seven years and countless servers later, SSH is still the tool I use more than anything else — and I've learned a lot of tricks that save me time every single day. Most of them come down to one file: ~/.ssh/config.
Here's the SSH configuration I actually use in production, and why each piece matters.
The Config File That Pays for Itself
Here's my ~/.ssh/config — I'll break down each section:
# Keep connections alive — no more "broken pipe"
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
# Connection multiplexing — one TCP connection, many sessions
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%C
ControlPersist 600
# My servers
Host production
HostName davideandreazzini.co.uk
User deploy
Port 22
IdentityFile ~/.ssh/prod_ed25519
Host staging
HostName staging.davideandreazzini.co.uk
User deploy
Port 2222
IdentityFile ~/.ssh/staging_ed25519
Host db-backup
HostName 10.0.1.50
User backup
IdentityFile ~/.ssh/backup_key
ProxyJump production
# Jump host for internal network
Host 10.0.1.*
ProxyJump production
User admin
# Quick local VMs
Host dev-*
User vagrant
IdentityFile ~/.ssh/vagrant_key
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
This single file replaces hundreds of command-line flags and makes SSH feel like it just works. Let me walk through each piece.
Connection Keepalive: Never Lose a Session Again
If you've ever left an SSH session idle for 10 minutes and come back to a frozen terminal that you can't even Ctrl-C out of, you need ServerAliveInterval:
ServerAliveInterval 60
ServerAliveCountMax 3
This sends an encrypted keepalive probe every 60 seconds through the SSH channel. If 3 consecutive probes go unanswered (180 seconds total), the connection is terminated cleanly. The important detail: these are encrypted-channel keepalives, not TCP-level ones. They can't be spoofed by a man-in-the-middle — unlike TCPKeepAlive, which operates at the network layer.
Before I added this, I'd regularly lose sessions to my VPS in Montevideo because the connection would silently drop and my terminal would hang indefinitely. Now, dead connections close within 3 minutes and I can reconnect immediately.
Connection Multiplexing: One Connection to Rule Them All
This is the single biggest quality-of-life improvement in my SSH setup:
ControlMaster auto
ControlPath ~/.ssh/sockets/%C
ControlPersist 600
Here's what this does: the first time I ssh production, SSH establishes a TCP connection, authenticates, and creates a Unix domain socket at ~/.ssh/sockets/%C (where %C is a hash of the connection parameters). Every subsequent ssh production command connects through that existing socket — no new TCP handshake, no authentication, instant.
The practical difference:
# Without multiplexing: each connection takes 1-3 seconds
$ time ssh production "hostname"
prod-server-01
0m1.84s real
# With multiplexing: subsequent connections are instant
$ time ssh production "hostname"
prod-server-01
0m0.03s real
That's a 60x speedup. When you're running commands on remote servers dozens of times a day, this adds up. And ControlPersist 600 keeps the master connection alive for 10 minutes after the last session closes, so I can SSH in, run a command, disconnect, and reconnect instantly within that window.
One setup note: you need to create the sockets directory:
mkdir -p ~/.ssh/sockets
To manage multiplexed connections, SSH provides handy control commands:
# Check if a master connection is active
ssh -O check production
# Close the master connection (and all sessions using it)
ssh -O exit production
# Forward a port through an existing connection (no new handshake)
ssh -O forward production
Host Aliases: Stop Typing Long Commands
Before my config file, connecting to production looked like this:
ssh -i ~/.ssh/prod_ed25519 -p 22 deploy@davideandreazzini.co.uk
Now it's just:
ssh production
The Host directive makes it a named alias. HostName is the actual server, User and Port are self-explanatory, and IdentityFile points to the specific key. Each of my servers gets its own identity file — I never use the same key for two different services.
ProxyJump: The Gateway Pattern
My database backup server doesn't have a public IP — it's only reachable through the production server. Before I knew about ProxyJump, I'd do this:
# Old way: two manual hops
ssh -L 5432:10.0.1.50:5432 production
# Then connect to the forwarded port...
With ProxyJump, it's one command:
ssh db-backup
SSH automatically tunnels through production to reach 10.0.1.50. The ProxyJump directive in the config handles it transparently. And with multiplexing, the jump connection reuses the existing socket to production — so even multi-hop SSH is instant.
The wildcard pattern Host 10.0.1.* means any IP in that subnet automatically routes through the jump host. I never have to remember which servers are internal — SSH figures it out.
Ed25519 Keys: Smaller, Faster, More Secure
OpenSSH 9.5 generates Ed25519 keys by default, and for good reason:
# Generate a new Ed25519 key (the modern default)
ssh-keygen -t ed25519 -C "davide@production" -f ~/.ssh/prod_ed25519
# Compare key sizes
$ wc -c ~/.ssh/prod_ed25519.pub
82 ~/.ssh/prod_ed25519.pub
$ wc -c ~/.ssh/old_rsa_4096.pub
726 ~/.ssh/old_rsa_4096.pub
Ed25519 keys are roughly 9x shorter than 4096-bit RSA keys, faster to verify, and based on elliptic curve cryptography that's resistant to known quantum computing attacks. There's no reason to use RSA keys for new deployments anymore.
I still have a few RSA keys for older servers that don't support Ed25519, but they're the exception. If you're generating a new key today, ssh-keygen -t ed25519 is the way to go.
Keystroke Timing Obfuscation (OpenSSH 9.5+)
This is a newer feature I've started using after the CVE incident on this very blog. OpenSSH 9.5 introduced ObscureKeystrokeTiming, which hides your typing patterns from network observers:
Host *
ObscureKeystrokeTiming yes
When enabled, SSH sends interactive traffic at fixed intervals (default: every 20ms) and injects fake keystroke chaff. This makes it significantly harder for anyone monitoring the encrypted SSH stream to infer what you're typing — which matters more than you'd think. Researchers have shown that keystroke timing analysis can reveal passwords and commands with surprising accuracy, even through an encrypted channel.
Given that someone already extracted my Ghost admin key from this server, I'll take every layer of protection I can get.
Quick Reference: My Most-Used SSH Flags
A few flags I use daily that aren't in my config file:
# Copy a file to a remote host (uses config aliases!)
scp backup.sh production:/opt/scripts/
# Or with rsync for large/synced transfers
rsync -avz --progress ./project/ production:/var/www/
# Forward a local port to a remote service
ssh -L 8080:localhost:80 production
# Forward a remote port to local (reverse tunnel)
ssh -R 9090:localhost:3000 production
# Execute a command without opening a shell
ssh production "df -h && free -m"
# Mount a remote directory locally with sshfs
sshfs production:/var/log /mnt/prod-logs
All of these work with the config aliases. scp, rsync, and sshfs all read ~/.ssh/config, so once your aliases are set up, they work everywhere.
One File, Massive Impact
The entire configuration lives in one file. It's version-controlled in my dotfiles repo. When I set up a new machine, I clone the repo, and every SSH connection works exactly the same way — same aliases, same keys, same keepalive settings, same multiplexing.
If you're still typing ssh -i key.pem -p 2222 user@some-ip every time you connect to a server, spend 10 minutes writing your ~/.ssh/config. It'll pay for itself by the end of the day. And if you're already using it, add ControlMaster auto and ControlPersist 600 — that one change will make you wonder how you ever lived without it.