Host key verification failed: six causes, one test
Host key verification failed. is the last line of an ssh run, not the first clue. OpenSSH prints it whenever it cannot confirm the server's identity against a key it trusts, and that one sentence covers at least six unrelated faults: an empty known_hosts in a fresh container, a cron job with no terminal to answer the prompt, a config block that never applies because of its position, and a key file that silently refuses the write and reports success anyway. This post reproduces each case against a real server, quotes the diagnostic line that separates them, and ends with a provisioning step that survives a redeploy.
Prerequisites: an OpenSSH client, a reachable host and port, and write access to your own known_hosts. These runs used OpenSSH 10.0p2 on Debian 13, aarch64, talking to a server that presents a freshly generated ED25519 host key on port 2222 — option names and defaults differ on older releases, so re-run the one-liners below on your own client before quoting them to a colleague.
One disclosure about the rig: this host has no sshd, so the server end is AsyncSSH 2.24.1 with a key generated by ssh-keygen. The client is genuine OpenSSH, and every string quoted below is emitted by that client — which is the side the error lives on. Server-side policy errors would look different.
TL;DR — the verdict is generic, the line above it is not
- The failure is always
Host key verification failed.with exit status 255. The diagnosis sits one line higher and only appears with-v:No ED25519 host key is known for [127.0.0.1]:2222 and you have requested strict checking. StrictHostKeyCheckingdefaults toask, andaskneeds a terminal. UnderBatchMode=yes— cron, CI, every Ansible task — the prompt cannot happen, so the default behaves likeyesand fails.ssh_configis first-match-wins. AHost *block placed aboveHost lab-*makes the specific block dead code, with no warning anywhere.UserKnownHostsFile=/dev/nullplusStrictHostKeyChecking=yesdoes not skip the check, it guarantees the failure./dev/nullonly makes sense withaccept-new.- Two widely repeated explanations are false on a current client: an entry stored without brackets does match a non-standard port, and an unset
HOMEdoes not break the write. accept-newagainst an unwritable file printsFailed to add the host to the list of known hostsand still exits 0. The run passes, nothing is stored, the next run fails.