Overview
SSH shows "Host key verification failed" when the server’s host key is missing from, or conflicts with, your known_hosts file. This protects you from man‑in‑the‑middle attacks. Fixes involve verifying the host’s fingerprint, then updating known_hosts.
Common contexts in DevOps/Git:
- First-time SSH to a Git remote (e.g., [email protected])
- CI/CD runners with ephemeral home directories
- Rebuilt servers or rotated SSH host keys
Quickstart (safe defaults)
- First connection to a known-good host:
- Interactively: ssh -o StrictHostKeyChecking=accept-new user@host
- Non-interactive (CI): ssh-keyscan -H host >> ~/.ssh/known_hosts
- Host key changed and you trust the new host key:
- Remove old key: ssh-keygen -R host
- Add new key: ssh-keyscan -H host >> ~/.ssh/known_hosts
For Git remotes, replace host with your Git server (e.g., github.com, gitlab.com).
Minimal working example (CI-safe Git clone)
This snippet preloads known_hosts and clones over SSH without interactive prompts.
set -euo pipefail
# 1) Ensure SSH directory and correct permissions
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 2) Preload host key(s) safely
# Optional: pin key types to modern algorithms
ssh-keyscan -t rsa,ecdsa,ed25519 -H github.com >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
# 3) Clone using SSH; no prompts
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=yes" \
git clone [email protected]:org/repo.git
Notes:
- Use StrictHostKeyChecking=yes after you preload known_hosts. Avoid "no"; prefer accept-new or preloading.
- Repeat ssh-keyscan for each host you access (e.g., github.com, gitlab.com, your.internal.git).
Step-by-step fixes
- Identify the cause
- First connect: host is not in known_hosts.
- Host key changed: mismatch with stored key.
- Wrong host name or DNS: connecting to an unexpected server.
- Verify the fingerprint
- Obtain the host’s SSH fingerprint via your organization’s source of truth or console. Compare to ssh-keyscan output:
- ssh-keyscan -H host | ssh-keygen -lf -
- Remove stale entries (only if a change is confirmed)
ssh-keygen -R host
# If a non-standard port, include it: ssh-keygen -R "[host]:2222"
- Add the correct host key
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keyscan -H host >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- Test SSH
ssh -o StrictHostKeyChecking=yes user@host exit
- Retry your Git operation
git ls-remote git@host:org/repo.git
# or
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=yes" git clone git@host:org/repo.git
Git-specific tips
- Non-interactive pipelines:
- Preload keys with ssh-keyscan.
- Or use accept-new if your OpenSSH supports it (7.6+): GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git fetch
- Multiple hosts: maintain a single known_hosts shared across jobs or cache it between pipeline stages.
- Custom ports: use the [host]:port format in known_hosts and ssh-keygen -R.
Using SSH config (host aliases)
Simplify commands and pin options per host.
# ~/.ssh/config
Host git-prod
HostName git.example.com
User git
Port 22
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking yes
UserKnownHostsFile ~/.ssh/known_hosts
Then:
git clone git@git-prod:org/repo.git
Common scenarios and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Host key verification failed on first connect | Host not in known_hosts | ssh-keyscan -H host >> ~/.ssh/known_hosts |
| Same error after server rebuild | Host key rotated/mismatch | ssh-keygen -R host; ssh-keyscan -H host >> ~/.ssh/known_hosts |
| Works locally, fails in CI | Ephemeral home, empty known_hosts | Preload with ssh-keyscan in pipeline before Git commands |
| Using non-standard port | Entry doesn’t match [host]:port | Use ssh-keyscan -p PORT -H host >> ~/.ssh/known_hosts |
| Intermittent DNS/CDN targets | Different backends/keys | Use vendor-published known_hosts or pin to stable endpoint |
Pitfalls to avoid
- Using StrictHostKeyChecking=no in production. This disables verification and exposes you to MITM.
- Appending duplicate or incorrect keys. Clean stale keys with ssh-keygen -R before adding new ones.
- Wrong file permissions. SSH may ignore insecure files; ensure 600 for known_hosts, 700 for ~/.ssh.
- Skipping fingerprint verification. Always verify when keys change.
- Assuming one key type. Some hosts publish multiple keys (rsa, ecdsa, ed25519). Collect them as needed.
Performance notes
- Pre-seed known_hosts once per workspace or container image to avoid repeated ssh-keyscan calls.
- For many hosts, batch scan in one go:
ssh-keyscan -H host1 host2 host3 >> ~/.ssh/known_hosts
- Reduce latency by reusing SSH connections when doing many Git operations:
# ~/.ssh/config
Host *
ControlMaster auto
ControlPersist 5m
ControlPath ~/.ssh/cm-%r@%h:%p
- Set a reasonable ssh-keyscan timeout for slow networks:
ssh-keyscan -T 5 -H host >> ~/.ssh/known_hosts
Troubleshooting checklist
- Confirm you’re connecting to the expected hostname and port.
- Verify fingerprints against a trusted source before updating.
- Remove stale keys with ssh-keygen -R host (and [host]:port if needed).
- Re-add with ssh-keyscan -H host and secure permissions.
- Retry SSH with StrictHostKeyChecking=yes or accept-new.
- For Git in CI, preload known_hosts before any git fetch/clone.
Tiny FAQ
- Is StrictHostKeyChecking=no ever OK?
- Avoid it. Use accept-new for first connections or pre-seed known_hosts.
- Where is known_hosts?
- Unix/macOS: ~/.ssh/known_hosts. Windows (Git for Windows/OpenSSH): %USERPROFILE%.ssh\known_hosts.
- How do I handle rotated keys at scale?
- Distribute updated known_hosts via config management or bake it into base images, then run ssh-keygen -R and redeploy.
- Which key type should I prefer?
- ed25519 is modern and fast. Collect all types the host publishes to avoid mismatches.
- What OpenSSH version supports accept-new?
- OpenSSH 7.6 and newer.