SSH Host Key Verification Failed Explained With Every Cause
Understand SSH Host key verification failed: every cause, from a changed key or unwritable known_hosts to a non-interactive session, with the fix for each.

The last line of several different errors is Host key verification failed., and the lines above it tell which one it is. Reading them in order resolves it. The mechanism is trust on first use: the client remembers each server's key and compares it on every visit. The special case of a changed key is in REMOTE HOST IDENTIFICATION HAS CHANGED. The checks assume OpenSSH 9.x.
Cause 1: the key changed
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Offending ED25519 key in /home/alice/.ssh/known_hosts:14
Host key verification failed.
The stored key and the presented key differ. Verify through a trusted channel, then ssh-keygen -R host and reconnect. Do not skip verification.
Cause 2: unknown host, nobody to ask
Host key verification failed.
ssh -o BatchMode=yes [email protected] true
ssh-keyscan -t ed25519 new-host.example.testIn a script, cron job, CI runner or with BatchMode=yes, ssh cannot ask "Are you sure you want to continue connecting?", so it fails with this bare line. Add the host key beforehand, from a source you trust: ssh-keyscan -H -t ed25519 host >> ~/.ssh/known_hosts. Scanning over an untrusted network trusts whatever answers, so in CI, fetch the expected fingerprint from your provider and compare, or keep a reviewed known_hosts file in the repository.
Cause 3: strict checking with an unknown host
Host *.internal.example.test
StrictHostKeyChecking accept-new
UpdateHostKeys yes
StrictHostKeyChecking yes refuses every unknown host without prompting. The value accept-new, available since OpenSSH 7.6, accepts and stores keys from hosts never seen but still refuses changed keys. It is a reasonable policy for automation against machines you create. Setting it to no disables checking and is the unsafe option.
Cause 4: known_hosts is unusable
ls -l ~/.ssh/known_hosts ~/.ssh
ssh-keygen -lf ~/.ssh/known_hosts | head -3
ssh -o UserKnownHostsFile=/tmp/kh-test [email protected]Failed to add the host to the list of known hosts (/home/alice/.ssh/known_hosts).
Host key verification failed.
If the file or directory is owned by root (after a sudo ssh), is read-only, or sits on a read-only file system such as a container with a locked home, the client cannot record the key and gives up. Fix ownership with chown -R $USER ~/.ssh and mode 600 on the file. A corrupt line makes ssh-keygen -lf complain; remove the bad line. In a container, mount a writable file or pass -o UserKnownHostsFile=/path.
Cause 5: the host presents several key types
ssh -o HostKeyAlgorithms=ssh-ed25519 [email protected]
ssh-keyscan host.example.test | ssh-keygen -lf -A server may offer RSA, ECDSA and Ed25519 keys. If the stored key is of a type the client no longer prefers, or the server removed that key type, the client is told about the mismatch. Old servers offering only ssh-rsa may fail with a different error, no matching host key type. Hosts reached by IP and by name have separate entries; CheckHostIP and HashKnownHosts affect this.
What the client is protecting you from
When an SSH connection opens, the server proves its identity with a host key. The client compares that key with the one it stored on an earlier visit. A match means this is the same server. A difference means either the server was rebuilt, or something between you and it is pretending to be the server. The client cannot tell which, so it refuses to continue. That refusal is the entire purpose of the check, and every shortcut that silences it, including StrictHostKeyChecking no and sending known_hosts to /dev/null, converts the protection into a formality.
The first connection to a new server is the weak point, because there is nothing to compare. The prompt that shows a fingerprint and asks for confirmation is a request for you to compare it with a trusted source. Few people do, and that is how most interception attacks on SSH would succeed in practice. Providers publish fingerprints for their servers, and for your own machines the console of the cloud provider often shows the key generated at first boot. Using that channel once makes every later connection safe.
Fleets, rebuilds and changing addresses
The check generates many false alarms in environments where machines are replaced often. A server rebuilt from an image gets a new host key unless the key is preserved, and an autoscaling group reuses addresses, so a name that pointed to one machine yesterday points to another today. Each case produces the changed-key warning, and a team that sees it daily learns to ignore it. The cure is to make host keys stable or verifiable: generate keys at image build and inject them from a secret store, or sign host keys with an SSH certificate authority and configure clients to trust the authority instead of each host.
With host certificates, clients hold a single line in known_hosts marked @cert-authority with the CA's public key, and any server presenting a key signed by that CA is accepted. New machines need no first-visit confirmation, replaced machines raise no warning, and an impostor cannot produce a valid signature. The setup takes an afternoon and removes the problem for all machines in the domain.
Scripts, CI and non-interactive use
Automation cannot answer prompts, so it needs the key in place before the first connection. Commit a known_hosts file for the hosts a pipeline talks to, reviewed like any other change, and point to it with UserKnownHostsFile. Pair it with StrictHostKeyChecking yes so the job fails when a key changes instead of silently accepting it. Re-scanning at the start of every run defeats the purpose, since the scan trusts whatever answers at that moment.
Hashed entries add a wrinkle. With HashKnownHosts yes, host names in the file are stored as hashes, so you cannot read which hosts are listed, but you can still search for one with ssh-keygen -F host. Remove an entry with -R rather than by editing the file; the command handles hashed names and keeps a backup as known_hosts.old.
A short troubleshooting order
Read the lines above the final message first, since they name the cause. Then run the connection with -v and look for the host key type offered and the file and line the client compared. Check permissions on the directory and file. If the key changed, confirm through another channel before removing the old entry. If there is no entry, add one from a verified source. Following this order resolves most cases in a few minutes and leaves the verification step intact, which is the property that makes SSH worth using.
Where this does not apply
Git over SSH, rsync and scp use the same mechanism, so the same fixes apply, and Git hosting providers publish their fingerprints on their documentation pages for you to compare. In containers and CI, shipping a reviewed known_hosts with the job is the cleanest answer. See the ssh_config StrictHostKeyChecking section and the ssh-keyscan manual. Related reading: debugging SSH authentication.


