Ansible "Failed to connect to the host via ssh: Permission denied (publickey)" in CI
The SSH transport reached the host and offered keys, but none were accepted, so the server rejected the login with "Permission denied (publickey)". In CI this almost always means the deploy key is missing from the runner, unreadable, or sent for the wrong remote user.
What this error means
A task or the play setup fails with "fatal: [host]: UNREACHABLE!" or a connection error wrapping "Failed to connect to the host via ssh: ... Permission denied (publickey)". Public-key auth fails for every host in the play.
fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the
host via ssh: ... Permission denied (publickey).", "unreachable": true}Common causes
No private key loaded on the runner
The deploy key was never written to the job or added to an agent, so Ansible offers no key the host accepts.
Wrong remote_user for the key
The key authorizes one account but ansible_user or remote_user points at another, so the server denies the offered key.
How to fix it
Provide the key from a secret and set the user
- Store the private key as a CI secret, never in the repo.
- Write it to a file with
0600perms and load it into ssh-agent, or pass--private-key. - Set the matching
ansible_userfor the target inventory.
- name: Load deploy key
run: |
install -m 600 /dev/stdin ~/.ssh/id_deploy <<< "${{ secrets.DEPLOY_KEY }}"
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_deploy
- run: ansible-playbook -i inventory site.yml -u deployConfirm the key and user against the host
Test the raw login first; if ssh -i key user@host is denied, Ansible will be too. Fix the user or authorized_keys before re-running the play.
ssh -i ~/.ssh/id_deploy -o IdentitiesOnly=yes deploy@web1 trueHow to prevent it
- Keep deploy keys in CI secrets and write them with 0600 permissions.
- Pin
ansible_userper host group so the key matches the account. - Use
IdentitiesOnly=yesso the agent does not offer unrelated keys.