Skip to content
LatchkeyLatchkey home

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

The SSH server rejected every key the client offered. In CI this almost always means no private key is loaded on the runner, or the key that is loaded was never added to the Git host or granted access to the repo.

What this error means

An SSH clone/fetch fails with git@github.com: Permission denied (publickey) and then fatal: Could not read from remote repository. It is deterministic - the same key fails the same way on every run.

git clone output
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Diagnose it: depth, refs, or credentials?

Terminal
git rev-parse --is-shallow-repository
git rev-parse --abbrev-ref HEAD    # prints HEAD when detached
git log --oneline -3
git remote -v

Common causes

No private key loaded on the runner

The runner has no SSH key in its agent or ~/.ssh, so the client offers nothing and the server falls back to "publickey" denial.

The key is not registered with the host or repo

A key exists but was never added as a deploy key on the repo or an SSH key on the account/org, so the host does not recognize it.

Wrong key permissions or no agent

A private key with loose permissions is ignored by SSH, and without a running ssh-agent the key is never offered at all.

How to fix it

Load the deploy/SSH key on GitHub Actions

Use the checkout action’s ssh-key input, or a dedicated action, to load a private key that has access to the repo.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    ssh-key: ${{ secrets.DEPLOY_KEY }}   # private key; add the public half as a repo deploy key

Set up the agent and key manually

If you manage SSH yourself, start the agent, add the key with correct permissions, and trust the host.

Terminal
eval "$(ssh-agent -s)"
install -m 600 /dev/stdin ~/.ssh/id_ed25519 <<< "$DEPLOY_KEY"
ssh-add ~/.ssh/id_ed25519
ssh -T git@github.com   # expect a success/auth message

Confirm the key is authorized

  1. Add the public key as a repo deploy key (or an account/org SSH key) with the right access.
  2. Verify with ssh -vT git@github.com and check which key is offered.
  3. Make sure the private key file is mode 600 and owned by the runner user.

How to prevent it

  • Use the checkout action’s ssh-key input instead of hand-rolling SSH.
  • Scope deploy keys per repo with the minimum access required.
  • Store the private key as a secret and never bake it into the image.

Frequently asked questions

What causes Git "Permission denied (publickey)" in CI?
There are 3 common causes: no private key loaded on the runner, the key is not registered with the host or repo, and wrong key permissions or no agent. The runner has no SSH key in its agent or ~/.ssh, so the client offers nothing and the server falls back to "publickey" denial.
How do I fix Git "Permission denied (publickey)" in CI?
There are 3 fixes depending on which cause you have: load the deploy/ssh key on github actions, set up the agent and key manually, and confirm the key is authorized. Work through them in order, since the first is the most common.
What does Git "Permission denied (publickey)" in CI actually mean?
An SSH clone/fetch fails with git@github.com: Permission denied (publickey) and then fatal: Could not read from remote repository.
How do I stop Git "Permission denied (publickey)" in CI happening again?
Use the checkout action’s ssh-key input instead of hand-rolling SSH. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card