Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for DevOps Security & Hardening By James Joyner IV · · 9 min read

SSH Error: 'Permission denied (publickey)' Key Authentication Failure

Quick answer

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
Free toolkit

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@host immediately returns Permission 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 as deploy).
  • The server log records Authentication refused: bad ownership or modes for file or no 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/ubuntu when the key lives in deploy’s account.
  • Permission/ownership problemssshd refuses authorized_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, or IdentitiesOnly not set so the agent runs out of tries before reaching the right key.
  • Key type disabled on the server — modern sshd rejects ssh-rsa (SHA-1) or DSA keys via PubkeyAcceptedAlgorithms, so an old key is offered but never accepted.
  • sshd policy excludes the userAllowUsers/AllowGroups/DenyUsers or a Match block blocks the account.
  • SELinux context on authorized_keys — a key file restored from a backup has the wrong label so sshd cannot 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, ~/.ssh 700, authorized_keys 600, all owned by the account. Leave StrictModes yes on.
  • Use IdentitiesOnly=yes per host. In ~/.ssh/config, pin one IdentityFile per host so the agent does not exhaust attempts and trigger Too many authentication failures.
  • Prefer Ed25519 keys. Retire ssh-rsa (SHA-1) and DSA keys, which modern servers reject by default; align client and server PubkeyAcceptedAlgorithms.
  • Manage keys as data. Provision authorized_keys through 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_keys sprawl and gives expiry and revocation.
  • Scope access with policy, not passwords. Use AllowGroups/Match blocks and keep PasswordAuthentication no so 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.

Free download · 368-page PDF

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.