Once key-only SSH is set up, the next suggestion that often comes up is two-factor authentication on top of the key, a TOTP code from an authenticator app, required in addition to the private key. It's presented as an obvious next step, more factors, more security. It's not wrong, but it's worth being specific about what threat this actually addresses, because that determines where it's worth the friction and where it isn't.
What it actually protects against
Key-only SSH already means a password being guessed, leaked, or phished doesn't get anyone in, there's no password to steal. SSH 2FA's additional protection is specifically against the private key itself being compromised, stolen from a laptop, copied from an unencrypted backup, or used from a machine that's been compromised in some other way. If that happens, the key alone isn't enough, the attacker also needs the TOTP code, which lives on a separate device.
That's a real scenario, laptops get stolen, backups get misconfigured, and "the key is on an encrypted disk so it's fine" assumes the disk encryption is actually configured and the laptop was locked. But it's a specific scenario, and it's worth being clear that it's the scenario being addressed, not "SSH security in general," which key-only auth has already addressed quite well.
The setup
pam_google_authenticator is the common way to add TOTP to SSH on Linux:
apt install libpam-google-authenticator
google-authenticator
This generates a secret (shown as a QR code for an authenticator app) and a set of backup codes. Then SSH and PAM need to be told to require it:
# /etc/pam.d/sshd
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config
AuthenticationMethods publickey,keyboard-interactive
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive is the important line, it requires both the key and the TOTP prompt, not either one. Getting this wrong (for instance leaving the default AuthenticationMethods unset) can result in a setup that accepts either factor alone, which defeats the purpose entirely, so this is worth testing in a second terminal before logging out of the first.
Where it gets in the way
The friction shows up in two places. First, every interactive login now needs a phone or authenticator app, not a huge deal for occasional logins, more noticeable if you're SSHing into a server dozens of times a day. Second, and more importantly, automation breaks. A CI/CD pipeline deploying via SSH, an Ansible playbook, a cron job that rsyncs to a remote host, none of these have a human available to type a TOTP code. Applying AuthenticationMethods publickey,keyboard-interactive server-wide breaks all of these simultaneously, often in a way that isn't obvious until the next scheduled job fails.
The middle ground that actually works
Rather than 2FA everywhere, the approach I use is to apply it only to interactive human logins, while automation uses separate, more restricted service accounts that aren't subject to the 2FA requirement, often because they're also restricted in other ways (a forced command in authorized_keys, or a key that can only run one specific script):
# /etc/ssh/sshd_config
Match User deploy,admin
AuthenticationMethods publickey,keyboard-interactive
Match User ci-deploy
AuthenticationMethods publickey
This way, the accounts a human uses to log in and run arbitrary commands get the extra factor, while the narrowly-scoped automation accounts, which couldn't realistically provide a TOTP code anyway, and which a compromise of a laptop wouldn't directly expose (their keys live in CI secrets, not on someone's laptop), keep working without it.
Where I land
For a server only ever accessed by automation, no human interactive logins at all, SSH 2FA doesn't add much, the threat it addresses (a stolen human-held key) doesn't really apply. For servers that people log into directly, especially ones with access to production data or the ability to make changes, the laptop-theft scenario is real enough that the relatively small added friction, a TOTP prompt on login, is worth it, as long as it's scoped to those accounts specifically and doesn't quietly break the automation that keeps everything else running.