What you see
In a script with BatchMode=yes, or with no terminal to ask on, there is only one line:
Host key verification failed.
With StrictHostKeyChecking=yes there is a reason above it:
No ED25519 host key is known for server and you have requested strict checking.
Host key verification failed.
Git adds its own lines, which do not mean the repository is missing:
Host key verification failed.
fatal: Could not read from remote repository.
Find out which case you have
Run this as the same user that runs the failing command:
ssh-keygen -F server
For a server on another port, the entry is stored with brackets:
ssh-keygen -F '[server]:2222'
- No output: the host is unknown to this user. See cause 1 and cause 2.
- A line is printed, and the error shows a large REMOTE HOST IDENTIFICATION HAS CHANGED warning: see cause 3.
Cause 1: the server is not in known_hosts
At a terminal, SSH would ask you to confirm the fingerprint. BatchMode=yes forbids questions, and a job with no terminal has nobody to ask, so SSH fails instead. Add the key ahead of time.
Collect the key, then compare it with the one on the server before you trust it:
ssh-keyscan -t ed25519 server > server.hostkey
ssh-keygen -lf server.hostkey
On the server itself, or through a console from your provider:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
If the two fingerprints match, install the key:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
cat server.hostkey >> ~/.ssh/known_hosts
For a port other than 22, use ssh-keyscan -p 2222 -t ed25519 server. ssh-keyscan alone proves nothing, because it accepts whatever answers at that address. The comparison is what makes it safe. Hosting services publish their fingerprints, for example GitHub in the page listed in the sources.
In CI
Save the verified line as a CI variable and write it at the start of the job, instead of scanning on every run:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
printf '%s\n' "$KNOWN_HOSTS" >> ~/.ssh/known_hosts
accept-new and no
StrictHostKeyChecking=accept-new saves a key it has never seen and still refuses one that changed. It trusts the first connection without checking, which is an acceptable trade for a throwaway machine on a network you trust. Avoid StrictHostKeyChecking=no: it also lets connections continue when the key has changed, which is the situation this check exists for.
What each line of the file holds: known_hosts explained.
Cause 2: another user or another HOME
known_hosts belongs to a user. It works in your terminal and fails in the job when the job runs as someone else: sudo ssh uses root's file, and cron, Docker builds and CI runners have their own home folder, often empty on each run.
whoami
echo "$HOME"
ssh -G server | grep -iE '^(userknownhostsfile|stricthostkeychecking) '
The last command prints which files SSH will read for that host, without connecting. Install the key for that user, or point the job at a file you control:
ssh -o UserKnownHostsFile=/path/to/known_hosts -o BatchMode=yes user@server true
Cause 3: the saved key is different
The output then starts with this warning, and the last two lines are:
Host key for server has changed and you have requested strict checking.
Host key verification failed.
A reinstall or a new server at the same address is the common reason, but this is also what an interception looks like. accept-new does not get past it. Compare fingerprints as shown above, and only then remove the old entry:
ssh-keygen -R server
With github.com this is common: GitHub replaced its RSA host key in March 2023, so an older saved line now fails. Remove it with ssh-keygen -R github.com, then add the keys GitHub publishes, from the page in the sources.
More on this case in REMOTE HOST IDENTIFICATION HAS CHANGED.
Questions
What does Host key verification failed mean?
SSH could not confirm the server's identity and stopped. Either the server is not in known_hosts and nobody could answer the prompt, or its key differs from the saved one.
Is StrictHostKeyChecking=no a safe fix in CI?
No. It also accepts a changed key. Install the verified key in known_hosts, or use accept-new on throwaway machines on a network you trust.
Why does it work in my terminal but fail in cron?
known_hosts belongs to a user. Cron, sudo and CI runners use another user or home folder, where the saved entry is missing.
Why does git say Host key verification failed with GitHub?
Either github.com is not in known_hosts for the user that runs git, or an RSA line saved before March 2023 is still there. Remove it with ssh-keygen -R github.com and add the keys from GitHub's fingerprints page.
Sources
- ssh_config(5), StrictHostKeyChecking
- ssh-keyscan(1), OpenBSD manual
- ssh-keygen(1), OpenBSD manual
- GitHub's SSH key fingerprints