SSH Error: 'Permission denied (publickey)' Key Authentication Failure
Fix SSH 'Permission denied (publickey)': diagnose key mismatch, wrong user, authorized_keys permissions, and sshd policy with verbose logging, then harden key-only auth safely.
- #security
- #hardening
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
SSH prints this when the server offered public-key authentication, the client tried every key it had, none was accepted, and no other method (password, keyboard-interactive) was available or allowed. The connection closes before a session starts:
user@bastion: Permission denied (publickey).
With client verbosity you see the negotiation fail after each key is refused:
debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:9k...
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
git@github.com: Permission denied (publickey).
This is an authentication failure. The client reached sshd and the transport handshake succeeded; the server simply refused every credential offered. It is the correct, secure default on a key-only host — the job is to find which link in the chain is wrong without weakening the policy.
Symptoms
ssh user@hostimmediately returnsPermission denied (publickey)and drops the connection.- Automation (Ansible, CI deploy keys,
git push) fails with the same message while an interactive login from another machine works. - A brand-new key or a freshly provisioned server rejects a login that “should” work.
- Login succeeds as one user but fails as another (e.g. works as
ubuntu, fails asdeploy). - The server log records
Authentication refused: bad ownership or modes for fileorno such identity.
Common Root Causes
- Key not installed for the target user — the public key is not in the remote account’s
~/.ssh/authorized_keys, or was pasted into the wrong user’s file. - Wrong username — connecting as
root/ubuntuwhen the key lives indeploy’s account. - Permission/ownership problems —
sshdrefusesauthorized_keys(or~/.ssh, or the home dir) if it is group/world-writable; strict mode rejects it silently. - Client offers the wrong key — no matching private key in the agent or
~/.ssh, orIdentitiesOnlynot set so the agent runs out of tries before reaching the right key. - Key type disabled on the server — modern
sshdrejectsssh-rsa(SHA-1) or DSA keys viaPubkeyAcceptedAlgorithms, so an old key is offered but never accepted. - sshd policy excludes the user —
AllowUsers/AllowGroups/DenyUsersor aMatchblock blocks the account. - SELinux context on authorized_keys — a key file restored from a backup has the wrong label so
sshdcannot read it. - Expired or CA-mismatched certificate — on a CA-based setup, the user certificate expired or was signed by an untrusted CA.
Diagnostic Workflow
Run the client in verbose mode to see exactly which keys are offered and how the server responds:
ssh -v user@host # add -vv / -vvv for more detail
Confirm which identity the client actually presents, and force it to use only the intended key:
ssh-add -l # keys in the agent
ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes user@host
On the server, read sshd logs while the client retries — the reason is usually explicit:
sudo journalctl -u ssh -f # Debian/Ubuntu (or -u sshd on RHEL)
Authentication refused: bad ownership or modes for file /home/deploy/.ssh/authorized_keys
Check the permission chain on the server for the target account:
ls -ld /home/deploy /home/deploy/.ssh
ls -l /home/deploy/.ssh/authorized_keys
# want: home not group/world-writable, .ssh 700, authorized_keys 600, owned by deploy
Verify the offered key’s fingerprint matches an installed key:
ssh-keygen -lf ~/.ssh/id_ed25519.pub # client
sudo awk '{print $2}' /home/deploy/.ssh/authorized_keys \
| while read k; do echo "$k" | ssh-keygen -lf -; done # server
Confirm the server is not rejecting the key’s algorithm or the user via policy:
sudo sshd -T | grep -Ei 'pubkeyacceptedalgorithms|allowusers|allowgroups'
Example Root Cause Analysis
A CI job that had deployed for months suddenly failed every run with Permission denied (publickey), while engineers could still SSH in interactively. ssh -v from the runner showed it offering the deploy key and the server answering Authentication refused: bad ownership or modes for file /home/deploy/.ssh/authorized_keys.
The cause was a misfired configuration-management run that had reset the home directory to 0775 and made it group-writable. sshd strict mode treats a group-writable home as untrusted and ignores authorized_keys entirely — which is exactly the protection that stops another local user from injecting keys. The fix restored ownership and modes:
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
The next CI run authenticated immediately. Loosening StrictModes no would also have “fixed” it — while permanently disabling a guard against key injection, so it was never on the table.
Prevention Best Practices
- Keep the permission chain tight. Home dir not group/world-writable,
~/.ssh700,authorized_keys600, all owned by the account. LeaveStrictModes yeson. - Use
IdentitiesOnly=yesper host. In~/.ssh/config, pin oneIdentityFileper host so the agent does not exhaust attempts and triggerToo many authentication failures. - Prefer Ed25519 keys. Retire
ssh-rsa(SHA-1) and DSA keys, which modern servers reject by default; align client and serverPubkeyAcceptedAlgorithms. - Manage keys as data. Provision
authorized_keysthrough configuration management with explicit owner/mode, so ad-hoc edits cannot drift permissions. - Adopt SSH certificates at scale. An SSH CA with short-lived user certificates removes per-host
authorized_keyssprawl and gives expiry and revocation. - Scope access with policy, not passwords. Use
AllowGroups/Matchblocks and keepPasswordAuthentication noso key-only auth stays enforced. - Restore SELinux contexts after moving key files:
restorecon -Rv ~/.ssh.
Quick Command Reference
# Client: see which keys are offered and force one key
ssh -v user@host
ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes user@host
ssh-add -l
# Server: watch the real reason
sudo journalctl -u ssh -f # or -u sshd
# Fix the permission chain for the target user
sudo chmod 700 ~/.ssh && sudo chmod 600 ~/.ssh/authorized_keys
sudo chown -R user:user ~/.ssh
# Confirm algorithm/user policy and effective config
sudo sshd -T | grep -Ei 'pubkeyacceptedalgorithms|allowusers'
# Match fingerprints between client key and server file
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# Restore SELinux label if applicable
restorecon -Rv ~/.ssh
Conclusion
Permission denied (publickey) means SSH did its job: every credential offered was refused and no fallback was allowed. Diagnose it from both ends — ssh -v on the client to see which key is offered, and sshd logs on the server to see why it was refused. The usual culprits are the wrong user, a key that is not in the target account’s authorized_keys, or a permission/ownership problem that trips strict mode. Fix the specific broken link and keep the hardening intact: never reach for PasswordAuthentication yes or StrictModes no to make the error disappear, because those guards are exactly what make key-only SSH worth running.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.