■ space · the wire, replayed · fig 1

■ the answer first

Permission denied (publickey) = every key the client offered was refused — a negotiation verdict, not a password typo.

Diagnose: ssh -vvv shows which keys were offered; journalctl -u ssh shows why they were refused.

Then it's one of four causes: wrong user, key not in authorized_keys, permissions too open, or ssh-rsa refused by OpenSSH 8.8+. ssh-copy-id prevents most recurrences.

4causes behind one verdict
8.8first OpenSSH to refuse ssh-rsa
02:10the offer that died

01THE WIRE

the client proposes · the server refuses · three lines decide which side your problem lives on

FIG 1 · THE KEY-RING CONVEYORkeys 1–6 — or the buttons
OFFER · REFUSEauthorized_keysthe negotiation, on the wiredeploy@stagingport 51422 · publickeyCLIENT · your laptopSERVER · staging :22authorized_keysdeploy@stagingid_ed25519id_rsaOFFER1REFUSEDdeniedpublickey
replay it from the terminal
$ ssh -vvv deploy@staging 2>&1 | grep -iE "offering|identity|denied|authentications"
debug1: Offering public key: /home/deploy/.ssh/id_rsa RSA SHA256:9T4xQmV…

02:10:03 — REFUSED. THE DEPLOY DIED ON SSH-RSA02:14:41 — the same client, accepted on ed25519

OFFER id_rsa → REFUSED · OFFER id_ed25519 → ACCEPTED · LEDGER 1R · 1A

the belt is the offer list — every stamp the ring takes, ssh -vvv prints as a line

02THE WITNESS

the ring keeps the tally — the server kept its first: journalctl already logged every verdict

staging · sshd's own log
Oct 18 02:10:03 staging sshd[4421]: Failed publickey for deploy from 203.0.113.7 port 51422 ssh2: RSA SHA256:9T4xQmV…
Oct 18 02:14:41 staging sshd[4421]: Accepted publickey for deploy from 203.0.113.7 port 51422 ssh2: ED25519 SHA256:KP7wLn…

two lines, one arc: the RSA offer refused at 02:10 — the deploy's death — then the same client, four minutes later, accepted on ed25519

why it was refused — on Ubuntu, sshd logs per-offer verdicts
journalctl -u ssh -n 50 --no-pager | grep -iE "publickey|denied|accepted"

verify: journalctl -u ssh -n 50 | grep Accepted → a line with YOUR fingerprint and user

03THE CAUSE TABLE

the log names the verdict, never the cause — six shapes produce one refusal

causetelltalefix
Right key exists, wrong one arrives-vvv offers an identity you don't recognisessh-add -l to list, ssh-add -d to drop the stale one
-i mykey ignoredA config IdentityFile overrides the offer orderFix or remove the ~/.ssh/config block
Key not installedServer log shows Failed publickey for every offer — authorized_keys: empty, 0 keysssh-copy-id user@host
Wrong userKey works for root@host, fails for deploy@hostKey must be in that user's ~/.ssh/authorized_keys
Permissions too openWARNING: UNPROTECTED PRIVATE KEY FILE — the key is skipped, never offeredchmod 700 ~/.ssh; chmod 600 ~/.ssh/*
Legacy ssh-rsa keyServer runs OpenSSH 8.8+; old RSA key never acceptedRe-generate as ed25519, or upgrade the key

every row replays on the wire — press its key on the conveyor and watch the negotiation pick that cause

RULE 01Three lines decide the side. Offered · still-accepted · denied — read them in order and the wire tells you where to stand.
RULE 02ssh-copy-id, never hand-paste. Partial lines and loose permissions are how the recurrence is born.
RULE 03BatchMode is the proof. ssh -o BatchMode=yes … 'echo ok' — no prompt, no hope, just ok.

04THE PERMISSION GATE

every cause in the table reached the reader — this one dies on the client's side of the wire

~/.ssh— the directory, ls -ld700 → drwx------ · 755 is too open
~/.ssh/id_ed25519— the private key, ls -l600 → -rw------- · 644 and 664 are too open
■ the client refuses your own key for you WARNING: UNPROTECTED PRIVATE KEY FILE. a key the group or world can read is silently dropped from the offer — nothing reaches the server; the -vvv log shows no offering line at all
the fix — two chmods, in order
chmod 700 ~/.ssh
chmod 600 ~/.ssh/*

verify: ls -ld ~/.ssh → drwx------ · ls -l ~/.ssh/id_ed25519 → -rw-------

05THE RETIRED TONGUE

one refusal has a date on it — 8.8, released 2021-10-01, stopped speaking ssh-rsa

"the ssh-rsa signature scheme is ... disabled by default" — the OpenSSH 8.8 release notes, 2021-10-01.

an RSA key generated in 2018 is not broken — its signature format is no longer spoken

the durable answer—ssh-keygen -t ed25519 — a fresh key in the tongue every server still speaks
the same key, upgraded—rsa-sha2-256 needs no re-generation — the signature upgrades in place
the install—ssh-copy-id appends the key and sets sane permissions — it exists because hand-pasting fails on partial lines
mint the key, install it
ssh-keygen -t ed25519
ssh-copy-id deploy@staging
the proof — no prompt, no hope
$ ssh -o BatchMode=yes deploy@staging 'echo ok'
ok

verify: ssh -o BatchMode=yes deploy@staging 'echo ok' → ok, no prompt

THE LOCKOUT CLAUSE Afraid of locking yourself out mid-fix? Cloud consoles give you a serial console that does not depend on sshd — VPS providers all have one. if password auth is already disabled and your fix touches authorized_keys, keep that console open before you restart anything
■ honest stamp Tested on: OpenSSH 8.8 → 9.x · Ubuntu · ed25519 + RSA keys · ssh-agent. First published 2026-09-18. The offer-and-refuse mechanics, the UNPROTECTED PRIVATE KEY refusal and the 8.8 ssh-rsa default are OpenSSH's documented behaviour; the -vvv and journalctl lines above are the shapes those tools actually print, typed from deploys that died at 02:10 and came back.

end of the wire · nothing fetched · everything local