Skip to content
Latchkey

Ansible "Permission denied (publickey)" - Fix SSH Auth in CI

SSH reached the host but rejected authentication - the private key the runner offered is not accepted for the user being used. This is a credentials/config problem, not a network one.

What this error means

Ansible reports the host UNREACHABLE with Permission denied (publickey). The connection itself succeeds (so it is not a timeout), but no offered key authenticates, so every task is skipped. It fails identically on retry.

ansible output
fatal: [db01]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to
the host via ssh: deploy@10.0.4.7: Permission denied (publickey).", "unreachable": true}

Common causes

Private key not loaded in CI

The deploy key is not added to the SSH agent or referenced via --private-key, so Ansible has no key to offer. Common when the key lives in a secret that was never written to disk or loaded.

Wrong remote_user or missing public key

The play connects as the wrong user, or the matching public key is not in that user’s authorized_keys on the target, so authentication is refused.

How to fix it

Load the deploy key into the agent

Write the secret key to the agent (or a file) before running the playbook.

.github/workflows/deploy.yml
- run: |
    eval "$(ssh-agent -s)"
    echo "${{ secrets.DEPLOY_SSH_KEY }}" | ssh-add -
    ansible-playbook -i inventory site.yml

Match remote_user and authorized_keys

  1. Set ansible_user / remote_user to the account whose authorized_keys holds your public key.
  2. Verify manually: ssh -i key deploy@host should succeed before Ansible does.
  3. Confirm the public key is present on the target for that user.

How to prevent it

  • Store the deploy private key as a CI secret and load it per job.
  • Pin ansible_user explicitly in inventory so the right account is used.
  • Use a dedicated, narrowly scoped deploy key rather than a personal key.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →