Permission denied (publickey) is the most common SSH failure there is, and the least helpful message SSH produces. It tells you authentication failed and refuses to say why, which is deliberate: explaining the failure to someone who has not authenticated would leak information about the account. Here is what it actually means and the order worth checking things in.
What does "Permission denied (publickey)" mean?
It means two things at once. The server offered public key authentication, your client tried whatever keys it had, and none were accepted. It also means the list in parentheses is the complete set of methods still available, so (publickey) on its own tells you password authentication is switched off on that server. If you were expecting to be asked for a password, that is your answer.
What it does not tell you is which side failed. Your client may never have offered the key you had in mind. Or it offered the right key and the server declined to read the file it should have matched against. Those are completely different problems with the same error message, and separating them is the first useful step.
Why does SSH reject my key when I know it is the right one?
Because "having the right key" is not the same as "offering the right key," and "the server has your key" is not the same as "the server will read the file it is in."
SSH only tries a fixed default set of key filenames (id_rsa, id_ecdsa, id_ed25519 and a few others) plus whatever your agent has loaded. A key saved as ~/.ssh/work-server is invisible unless you point at it with -i or configure it in ~/.ssh/config. People lose a lot of time to this, because the key is right there and clearly correct.
On the other side, sshd refuses to read authorized_keys if the permissions around it are too loose, and it does so silently. From the client you see the same generic denial. This is the single most common server-side cause.
How do you fix Permission denied (publickey)?
1. Check the username
Trivial and genuinely the most frequent cause on cloud hosts, because the account is not what people expect. Ubuntu images use ubuntu, Amazon Linux uses ec2-user, Debian uses admin or debian, and many images disable root for SSH outright. A correct key against the wrong username produces exactly this error.
2. Check the permissions on the server
This is the classic. sshd runs a check called StrictModes and will ignore your authorized_keys completely if anything in the path is writable by anyone but the owner:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 755 ~
The home directory catches people out. If it is group writable, which happens on shared systems and after a careless chmod -R, key auth stops working and nothing in the client output hints at why.
3. Check the permissions on your own key
The client is strict too. A private key readable by other users is refused with "WARNING: UNPROTECTED PRIVATE KEY FILE" and SSH will not use it:
chmod 600 ~/.ssh/id_ed25519
This one bites most often after copying keys from another machine, out of a backup, or across a filesystem that does not carry Unix permissions, such as a FAT USB stick or some mounted network shares.
4. Check the key is actually on the server, in the right form
The public key belongs in ~/.ssh/authorized_keys on the server, one key per line, no line breaks inside a key. A key pasted through an editor that wrapped it is a dead key. The reliable way to install one:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
If you cannot yet get in to run that, you need another way onto the box, covered further down.
5. Check which key is being offered
Name it explicitly and stop SSH from trying anything else:
ssh -i ~/.ssh/thekey -o IdentitiesOnly=yes user@host
Without IdentitiesOnly=yes, SSH still offers agent keys first even when you passed -i, which is also how you end up at Too many authentication failures: every offered key counts against the server's MaxAuthTries, and a well-stocked agent burns through the limit before reaching the key you meant.
6. Check the key type is still accepted
A modern cause that looks bizarre if you have not met it. OpenSSH 8.8 and later disable the old ssh-rsa signature algorithm, which uses SHA-1, by default. An RSA key that worked for years starts failing the moment a server is upgraded. Confirm with ssh -vvv, and either move to an ed25519 key, which is the right long-term answer:
ssh-keygen -t ed25519
or, as a temporary measure while you migrate:
ssh -o PubkeyAcceptedAlgorithms=+ssh-rsa user@host
7. Check the agent
ssh-add -l lists what your agent currently holds. "The agent has no identities" explains a lot of mysterious failures, particularly after a reboot on Linux where nothing re-adds keys automatically. Load it with ssh-add ~/.ssh/id_ed25519. On macOS, ssh-add --apple-use-keychain makes it survive restarts.
8. Check for SELinux and unusual home directories
On RHEL, Fedora, CentOS, and Rocky, SELinux needs the right context on the SSH files, and a manually created ~/.ssh often lacks it. restorecon -R -v ~/.ssh fixes it. Similarly, home directories on NFS, or an AuthorizedKeysFile pointed somewhere non-standard in sshd_config, both produce the same silent failure.
How do you debug it with ssh -vvv?
Run the failing command again with verbose output and read for two specific things:
ssh -vvv user@host
First, every line reading Offering public key: .... That is the definitive list of what your client actually tried. If the key you expected is not in that list, the problem is entirely client side and steps 5 and 7 are where to look. Second, the line Authentications that can continue: publickey, which confirms what the server will entertain.
Seeing your key offered and immediately followed by another attempt means the server looked and said no, which points at steps 2, 4, 6, and 8. That single distinction, offered-and-rejected versus never-offered, splits the eight causes above into two halves and is worth checking before anything else.
If you have root on the server, its log is more direct than anything the client will tell you. sudo tail -f /var/log/auth.log on Debian and Ubuntu, or sudo journalctl -u sshd -f on RHEL-family systems, will name the real reason, including the StrictModes rejection that is invisible from the client.
What if you are locked out completely?
If key auth is the only method enabled and the key no longer works, SSH cannot help you and you need a path that does not go through it. Every major cloud provider offers one: a serial or emergency console in the web dashboard, which gives you a login prompt over an out-of-band channel. AWS has EC2 Serial Console, Hetzner and DigitalOcean have web consoles, and most VPS providers offer a rescue mode that boots a recovery image with your disk mounted so you can fix authorized_keys and its permissions directly.
On hardware you can physically reach, a keyboard and monitor solve it. On a Raspberry Pi, pulling the SD card and editing the file from another machine is usually fastest.
The lesson people take away from a lockout is worth taking early: add a second key from a different device before you need it, and if you are about to change anything in sshd_config, keep your current session open while you test the new one from a second terminal. A working session is not affected by a broken config until you disconnect.
The short version
Run ssh -vvv and find out whether your key is being offered at all. If it is not, it is a client problem: wrong path, missing from the agent, or a deprecated key type. If it is offered and rejected, it is a server problem: wrong username, missing from authorized_keys, or, most likely, permissions on the home directory or ~/.ssh that make sshd refuse to read the file.
Worth noting that this entire class of failure comes from humans moving key material around by hand, and it is avoidable. Onepilot pairs a server by running one command on the host and scanning the code it prints, so there is no key to generate, chmod, paste, or lose, and the single-use pairing key is replaced by the app's own device key on first connect. If your problem is that the connection never opens rather than that it is refused, see SSH connection refused, and if it opens and then dies, see why SSH sessions keep disconnecting.
