SSH Permission Denied (publickey) — Find Which Key Was Rejected
Do not generate a new SSH key just because you see Permission denied (publickey). First prove the connection target and login user, then follow the intended key through the actual authentication transition: available → selected/offered → candidate accepted or rejected → signature succeeds → signed authentication accepted. Only then change client identity or server authorization. An “offered” key alone is not proof that public-key authentication completed.
Permission denied (publickey) or a list including publickey.You expect public-key authentication to work for this host/user.You need to know whether the problem is client key selection or server/account authorization.ssh -v user@hostLook for the resolved host/user and the authentication lines around Offering public key, Server accepts key, Authenticated to, or another Authentications that can continue. Exact wording varies by OpenSSH version. Also preserve any line that says the agent could not sign, a key/signature algorithm was refused, or another authentication method is still required—those are different branches from “the public key is simply absent from authorized_keys.”
Guardrail: Do not generate a new SSH key until verbose output proves the client lacks or cannot offer the intended key. A rejected key may already be perfectly valid cryptographically but unauthorized for this account.
Verify: you can state which host/user SSH is targeting and place the intended key at the furthest proven transition: absent, available-but-not-offered, offered/tested, candidate accepted but signing failed, signed request rejected, or fully authenticated.
Diagnostic flow — target → key → authorization
Which hostname/IP and port did SSH actually reach?
Which remote account is being authenticated?
Does the intended private key exist or appear in the agent?
Does verbose output prove that exact identity is offered?
Separate server acceptance of the key candidate from client signing and final acceptance of the signed authentication request.
Fix #2 — verify the target and remote user before touching keys
A public key authorizes an account on a particular SSH service; it does not grant universal access to a host. Check command-line user@host, Git remote URLs, host aliases and effective SSH configuration. Hosted Git services can require a service-specific SSH username—for example, GitHub's SSH endpoint uses git, not your GitHub account name.
ssh -G host | grep -E '^(hostname|user|port|identityfile) '
git remote -vssh -G prints the client's evaluated configuration; it is useful for checking values such as HostName, User, Port and IdentityFile, but it does not prove that a key was successfully loaded, signed with, offered, or accepted at runtime. Keep ssh -v as the runtime source of truth.
Verify: Verbose output connects to the intended host/port and shows the intended remote user before authentication begins.
Fix #3 — prove the intended private key is available
ssh-add -l shows identities currently held by the agent; it does not prove that a particular host connection will offer them. Also check whether the intended private-key file exists if you expect file-based discovery or an IdentityFile rule.
ssh-add -l -E sha256
ssh -G host | grep -i '^identityfile 'If the expected key is absent, determine whether it already exists under a non-default name, needs to be loaded into the appropriate agent, or should be referenced by host-specific config. Remember that configured IdentityFile entries and agent identities interact: OpenSSH can use agent identities in addition to configured identity files unless IdentitiesOnly restricts the set. Generate a new key only when you have established that the required identity truly does not exist or intentionally needs replacement.
Verify: You can identify the intended key by path or fingerprint without exposing the private key.
Fix #4 — key exists but SSH does not offer it
Test the intended identity explicitly. -i names an identity file; when multiple agent identities or config rules interfere, IdentitiesOnly=yes can restrict authentication to configured/explicit identities for the test.
ssh -v -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 user@hostIdentitiesOnly=yes is a selection control, not a repair by itself: it tells OpenSSH to use configured/explicit identities rather than every identity an agent may offer. If this controlled test works, make the selection host-specific in ~/.ssh/config rather than relying on a long global agent list. If your original error is instead Too many authentication failures, use the sibling guide because the server may disconnect before the intended identity is reached.
Verify: verbose output now contains the intended identity/fingerprint. Continue reading: determine whether the server rejects the candidate, accepts it but the client/agent cannot sign, rejects the signed authentication, requires another authentication method, or reaches Authenticated to.
Fix #5 — correct key is offered but the server rejects it
Once the intended key is demonstrably selected/offered, stop changing client key discovery unless the verbose transition shows a client signing problem. Verify that the matching public key is authorized for the same remote user and connection context. On a server you administer, inspect the account's effective sshd configuration—not only the first global line or an unqualified config dump. Match rules can depend on criteria such as user, source address/host and local address/port. Where supported, use sshd -T with the appropriate -C connection parameters to evaluate the context that actually failed. AuthorizedKeysFile, AuthorizedKeysCommand, PubkeyAuthentication, AuthenticationMethods and StrictModes can all alter acceptance. Check ownership/modes only against that effective policy and actual authorized-key source.
authorized_keys permissions are wrong.Do not loosen permissions broadly with chmod 777, disable StrictModes as a first fix, or replace authorized_keys wholesale. Preserve another working administrative session while changing remote access.
For managed Git/SSH services, compare the offered key's fingerprint with the key registered to the intended account/organization and consider service-side access policy. You may not have filesystem access to authorized_keys at all.
Verify: Retry the exact connection and confirm the server accepts the intended key for the intended account—without weakening authentication policy.
Fix #6 — key is offered, but signing or algorithm/policy fails
Do not collapse every post-offer transition into “wrong authorized_keys.” In the SSH public-key protocol, identifying/offering a key and completing a signed authentication request are distinct evidence points. Verbose output may show that the server is willing to consider a key but the agent cannot sign, that the client/server refuses a key or signature algorithm, that the signed request is rejected, or that public key is only the first step of an AuthenticationMethods chain. Preserve the exact transition and diagnose that branch rather than generating another key blindly.
For old RSA deployments in particular, distinguish the key from the signature algorithm negotiated for authentication. Avoid globally re-enabling legacy algorithms as a first response; prefer a supported server/client upgrade or the narrowest host-specific compatibility change when one is truly required.
Verify: The intended key can sign successfully, the negotiated authentication policy permits it, and verbose output progresses past the previously failing policy/signing step.
Fix #7 — the key is accepted: stop troubleshooting public-key authentication
If verbose output shows successful authentication, a later git push, shell, command, or SFTP failure is a different layer. Repository permissions, forced commands, shell restrictions and SFTP subsystem startup can all fail after identity authentication succeeds.
Verify: Run the exact connection/test expected by the service and separate authentication success from the next failing operation.
What not to do
chmod 777 on ~/.ssh or authorized_keys.Do not disable StrictModes or public-key security controls merely to make the error disappear.Do not assume ssh-add -l proves the target connection actually offered that identity.Do not assume every offered-but-rejected key means bad authorized_keys permissions; inspect effective sshd policy and server logs when available.Do not globally re-enable deprecated/legacy SSH algorithms just to clear one host's authentication error.Do not use sudo git ... casually: elevation can change the user, home directory, agent/environment and therefore the identities available to SSH.Do not treat “Offering public key” as proof the key completed authentication; follow candidate acceptance, signing and final signed-request acceptance.Do not treat an unqualified sshd -T/global config view as proof of policy under Match rules; evaluate the actual user/source/local connection context when you administer the server.Do not overwrite authorized_keys while diagnosing one key; preserve existing working access and add/remove authorization deliberately.Do not keep diagnosing public-key selection once verbose output reaches Authenticated to; a later shell, Git, forced-command or SFTP failure is a new layer.Still seeing Permission denied (publickey)?
Capture the authentication transition rather than the entire noisy log. The key question is where the chain first diverges: target, user, availability, offer, rejection or authorization.
ssh-add -l lists the identityredacted verbose transition for the intended fingerprint: selected/offered, candidate acceptance/rejection, signing result, signed-request result and final authentication stateserver-side sshd authentication log/journal plus effective sshd authorization policy evaluated for the failing user/source/local connection context when you administer the serverany agent-signing, key/signature-algorithm or AuthenticationMethods line near the rejectionwhether the same key works for another account/host and whether another key works for this accountOfficial references
Still stuck? Ask the community
Share the target type, remote user, redacted verbose authentication transition and key fingerprint—not the private key.
Loading community discussion…
Comments could not load here. Open ErrorHarbor Discussions on GitHub →