Drop #780 (2026-06-19): SSHHHH šŸ¤«šŸ“

SSH Tidbits You Might Not Know About

We’ve got a single-topic Drop today as I’ve been leaning into some SSH features that I should have been using more, but have had to deliberately use both for myself, and when creating skills for agents (so they behave better in various contexts).

Hence, no TL;DR, but I am willing to bet every reader will find at leastĀ oneĀ item you may not know (or have forgotten about), and that you will find regular uses for.


Photo by Kvalifik on Unsplash

Make connections fail fast instead of hanging

BatchModeĀ is the one that bites everyone who writes automation. SSH’s default behavior is chatty: if key auth isn’t set up, it falls back to prompting for a password, which is fine when you’re sitting at a keyboard and catastrophic when a cron job, CI pipeline, or Ansible run hits a misconfigured host – the connection just hangs, waiting for input that never comes.

BatchMode=yesĀ turns all of that off. No password prompts, no passphrase prompts, no ā€œare you sure you want to continue connecting?ā€ If key-based auth works, you connect; if it doesn’t, you get an immediate error and carry on with your life.

ssh -o BatchMode=yes user@hostname

Or in your config:

Host myserver
HostName example.com
BatchMode yes

The important nuance:Ā BatchModeĀ doesn’t disable key auth, it disablesĀ prompts. A valid key inĀ ssh-agentĀ or passed viaĀ -iĀ works exactly as normal – only the interactive fallbacks get suppressed. Pair it withĀ StrictHostKeyChecking=accept-newĀ to skip the unknown-host-key prompt too, and you’ve got a fully non-interactive connection:

ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new user@hostname

In practice this is the difference between a script that fails cleanly and one that wedges your whole pipeline:

#!/bin/bash
if ssh -o BatchMode=yes -o ConnectTimeout=5 user@server "uptime"; then
echo "Connection successful"
else
echo "SSH failed – check your key setup"
exit 1
fi

(One Debian/Ubuntu quirk:Ā BatchMode=yesĀ there also nudgesĀ ServerAliveIntervalĀ to 300s, which helps detect dead connections in long batch jobs.)

Teleport a shell function to a remote host

This one feels like cheating. Define a function locally, run it on a remote box, copy nothing:

hello() {
echo "Hello from $(uname -n)"
}
ssh user@server "$(declare -f hello); hello"

declare -fĀ serializes the function definition into text, which gets evaluated on the remote side before you call it. Tools likeĀ kittenĀ andĀ promptcmdĀ use exactly this trick to carry their behavior into remote sessions. Once you’ve internalized it, you’ll find yourself shipping ad-hoc helpers over SSH constantly.

Always land inĀ screen/tmux

If you live inĀ tmuxĀ (which I am using more thanks to Oh My OpenAgent) orĀ screen, why retypeĀ tmux attachĀ (orĀ screen -DR) every time you log in? Put this in your config:

Host myserver
RemoteCommand tmux new -A -s main
RequestTTY yes

or:

Host myserver
RemoteCommand screen -DR main
RequestTTY yes

ssh myserverĀ now drops you straight into (or reattaches to) a session namedĀ main. Disconnect – flaky wifi, closed laptop, whatever – and everything keeps running, waiting for you when you come back.

Copy yourĀ terminfo, not just your key

Everyone (hopefully) knowsĀ ssh-copy-id user@serverĀ for getting your public key onto a box. Fewer people know the companion move: shipping yourĀ terminfoĀ over so fancy terminals behave correctly on the remote side.

infocmp | ssh user@server -- tic -

This sends your terminal’s capability database to the server and compiles it there. It’s the fix for that ā€œNot using a fully-functional terminalā€ complaint from vim and less when you’re on a host that’s never heard of your terminal emulator.

I’m using this (more) now that I’ve fully migrated toĀ libghostty-backed terminals from WezTerm.

Make config conditional with Match

(I’ve mentioned this before but I’m re-upping it since it’s so stupid handy.)

HostĀ blocks match on hostname patterns.Ā MatchĀ blocks go further – they can key off the user, or even the result of an arbitrary command:

# Only jump through the bastion when the VPN is reachable
Match host *.internal exec "ping -c1 -W1 vpn.example.com >/dev/null"
ProxyJump bastion
# Use a different identity when connecting as admin
Match host server User admin
IdentityFile ~/.ssh/id_ed25519_admin

TheĀ execĀ form is the sleeper feature here: your SSH config can make decisions based on live conditions instead of static patterns.

Split your config into pieces

A single sprawlingĀ ~/.ssh/configĀ eventually becomes unmanageable.Ā IncludeĀ lets you break it apart:

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

The practical payoff is separation: work hosts in a gitignored file, personal hosts in your dotfiles repo, and no more accidentally leaking client hostnames into version control.

Eyeball šŸ‘€ your host keys

Turn on visual fingerprints and SSH draws a little ASCII-art rendering of the host key every time you connect:

Host *
VisualHostKey yes

We humans are bad at comparing hex strings and good at spotting when a picture looks wrong. If a host you connect to daily suddenly shows different art, your brain notices before your conscious mind does.

Kill a frozen session

When a session locks up hard and Ctrl-C, Ctrl-D, and Ctrl-Z all do nothing, there’s an escape hatch built right into the protocol:

Enter ~ .

Press Enter, then tilde, then dot, in sequence. That’s the SSH escape sequence for ā€œterminate this connection now.ā€ It’s saved me from opening a second terminal more times than I can count.

I used this enough during travel this week that I felt compelled to add it today’s Drop.

Stop getting silently dropped

NAT timeouts and idle firewalls love to kill SSH sessions out from under you. Keepalives fix it:

Host *
ServerAliveInterval 60
ServerAliveCountMax 3

This sends a keepalive through theĀ encrypted channelĀ every 60 seconds – distinct from TCP keepalive, which lives below the encryption and can be spoofed. If three go unanswered, SSH disconnects cleanly instead of freezing into a zombie session.

Run one sudo command and bail

For one-off privileged commands, you don’t need an interactive session:

ssh -tt user@server -- sudo systemctl restart nginx

The doubledĀ -ttĀ forces TTY allocation soĀ sudoĀ can prompt for its password. The command runs, sudo gets its prompt, the session exits. Clean.


If you want to really level things up a bit, aĀ Host *Ā block with a bunch of these (and other) SSH options canĀ quietly eliminate a dozen tiny annoyances from every connection you make for the rest of the day.


FIN

Remember, you can follow and interact with the full text of The Daily Drop’s free posts on:

  • Mastodon viaĀ @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev
  • Bluesky viaĀ <https://bsky.app/profile/dailydrop.hrbrmstr.dev.web.brid.gy>

ā˜®ļø

Leave a Reply

Discover more from hrbrmstr's Daily Drop

Subscribe now to keep reading and get access to the full archive.

Continue reading