Start with verbose output
-vvv shows which identities the client considered, which keys it offered and how the server responded. That sequence tells you where authentication stopped.
ssh -vvv git@github.com
ssh -vvv -i ~/.ssh/id_ed25519 user@server.example.com
Look for a sequence like this:
debug1: Offering public key: /home/you/.ssh/id_ed25519 ED25519 SHA256:AbC...
debug1: Authentications that can continue: publickey ← rejected, trying next
debug1: No more authentication methods to try.
Permission denied (publickey).
| What you see | What it means |
|---|---|
No Offering public key lines at all | The client found no usable key to offer; see cause 2 |
Offering… then Authentications that can continue | The server rejected the offered key; see cause 1 |
Server accepts key then failure | The key test passed, but a later authentication or authorization step failed; see cause 5 |
Bad permissions, UNPROTECTED PRIVATE KEY FILE | The client rejected a key because of its file mode; see cause 3 |
send_pubkey_test: no mutual signature algorithm | The client and server do not share an allowed signature algorithm; see cause 6 |
Cause 1: the server does not have your public key
When the trace shows a key being offered and then rejected, compare it with the entries in that account’s ~/.ssh/authorized_keys. The matching public key may be absent, malformed or installed for a different user.
# Install the key and set its permissions automatically
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Install manually when another access path is available
cat ~/.ssh/id_ed25519.pub | ssh user@server \
'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
Compare fingerprints instead of relying on filenames. This establishes whether the exact key offered by the client appears in the server file:
# Read the local key fingerprint
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# Read every fingerprint accepted by the server
ssh-keygen -lf ~/.ssh/authorized_keys
Cause 2: no key was offered
If the trace contains no Offering public key line, the client found no identity it could send. Confirm that a private key exists and that SSH knows its path.
ls -la ~/.ssh/
# Create a key when none exists
ssh-keygen -t ed25519 -C "you@example.com"
By default, SSH looks for names such as id_rsa, id_ecdsa and id_ed25519. A key named work_key needs an IdentityFile entry or an explicit -i argument. Put recurring host-specific choices in ~/.ssh/config:
# Client settings in ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_ed25519
IdentitiesOnly yes
Host prod
HostName 10.0.1.42
User deploy
IdentityFile ~/.ssh/prod_ed25519
IdentitiesOnly yes
Cause 3: file permissions
The client refuses a private key that other users can read. On the server, sshd can ignore authorized_keys when the file or a containing directory is writable by other users. The remote client often receives only the generic authentication error.
# Secure files on the client
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config
# Secure files on the server
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh
# Reject group and world writes to the home directory
chmod 755 ~
Remember to inspect the home directory as well as .ssh. If ~ is mode 775 or 777, sshd may refuse to trust authorized_keys; the useful explanation appears in the server log rather than the client error.
Cause 4: the wrong username
Unless configured otherwise, SSH uses the local username for the remote account. Public keys are installed per account, so a correct key offered for the wrong username is still rejected.
# Git hosts expect their service account
ssh -T git@github.com # Correct service account
ssh -T jackson@github.com # Wrong account
# Cloud images use different default accounts
ssh ubuntu@… # Default for Ubuntu on AWS
ssh ec2-user@… # Default for Amazon Linux
ssh admin@… # Default for Debian
ssh core@… # Default for Flatcar or CoreOS
A successful GitHub test prints Hi username! You've successfully authenticated, but GitHub does not provide shell access. The lack of shell access is expected; the message confirms that the key was accepted.
Cause 5: the agent, and forwarding
# List identities held by the agent
ssh-add -l
# Fix "Could not open a connection to your authentication agent"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Keep the passphrase in the macOS keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
On macOS, the following host defaults load the named key through the agent and Keychain:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
For a connection through a bastion, agent forwarding avoids placing the private-key file on the intermediate host. Copying a private key to a shared jump box gives that machine direct access to the credential:
Host bastion
HostName bastion.example.com
User jump
ForwardAgent yes
Host prod-*
ProxyJump bastion
User deploy
Cause 6: your key format is too old
OpenSSH 8.8, released in 2021, disabled ssh-rsa signatures using SHA-1 by default. An older RSA setup may therefore stop working after an upgrade and report this distinctive message:
debug1: send_pubkey_test: no mutual signature algorithm
Generate an Ed25519 key when both ends support it. It is smaller and faster than legacy RSA configurations and does not depend on SHA-1 signatures:
ssh-keygen -t ed25519 -C "you@example.com"
If rotation must be delayed, re-enable the legacy algorithm only for the affected host and keep the exception temporary:
Host legacy-server
PubkeyAcceptedAlgorithms +ssh-rsa
HostKeyAlgorithms +ssh-rsa
GitHub removed ssh-rsa support in 2022, and OpenSSH 9.8 removed DSA support entirely. Replace keys and algorithms that depend on those legacy options.
When you have access to the server
# Read the server reason hidden from the client
sudo tail -f /var/log/auth.log # Debian or Ubuntu log
sudo journalctl -u sshd -f # Systemd log
# Inspect the effective configuration
sudo sshd -T | grep -Ei 'pubkey|authorizedkeys|permitroot|allowusers'
# Validate changes before restarting the service
sudo sshd -t
A valid key can still be rejected by the effective server configuration. Check these settings in the sshd -T output:
PubkeyAuthentication no: public-key authentication is disabled.AllowUsersorAllowGroupsthat does not include your account.PermitRootLogin nowhen you are connecting as root.AuthorizedKeysFilepointing somewhere other than.ssh/authorized_keys.- Incorrect SELinux contexts on RHEL-family systems; repair them with
restorecon -R -v ~/.ssh.
Frequently asked questions
Why does permission denied (publickey) appear even though my key is correct?
A valid key can be installed for a different remote user, ignored because of file permissions or absent from the client’s offered identities. Run ssh -vvv. If it shows no "Offering public key" line, inspect client-side identity selection before changing the server.
How do I know which key SSH is using?
ssh -vvv prints each offered key and its fingerprint. Compare that value with ssh-keygen -lf on the local public key and with the fingerprints from the server’s authorized_keys file.
Why did my RSA key stop working?
OpenSSH 8.8 disabled SHA-1 RSA signatures by default, and GitHub removed ssh-rsa support in 2022. Generate an Ed25519 key: ssh-keygen -t ed25519. Re-enabling ssh-rsa is possible per-host but should be a stopgap only.
What permissions should ~/.ssh have?
Use 700 for the .ssh directory, 600 for private keys and authorized_keys, and 644 for public keys. The home directory must not be group- or world-writable; 755 or stricter is appropriate. Check server logs when sshd ignores an overly permissive path.
Why does it work with -i but not without?
SSH only auto-loads keys named id_rsa, id_ecdsa and id_ed25519. A differently named key must be declared with IdentityFile in ~/.ssh/config, ideally with IdentitiesOnly yes so it is the only key offered.
Can I use the same key on several machines?
The protocol allows it, but separate keys per device give you better revocation boundaries. Keep each private key on the device that generated it and install all required public keys on the server. Losing one device then requires revoking only its key.