You run git push or ssh and get one line back:

git@github.com: Permission denied (publickey).

The message means the server rejected every key your SSH client offered, or your client offered no key at all. It doesn’t say which, and that’s why it’s frustrating. This guide shows you how to find the cause in two commands and fix it, whether the server is GitHub, GitLab, or a Linux machine you run yourself.

Terminal output showing the Permission denied (publickey) error

What Causes Permission denied (publickey)?

Six causes account for nearly every case:

  • Wrong user. You connected as your own username instead of git.
  • No key. You haven’t created an SSH key on this machine.
  • Key not loaded. The key exists, but ssh-agent doesn’t hold it and your SSH config doesn’t point to it.
  • Key not registered. The server doesn’t have your public key.
  • Wrong key offered. You have several keys, and SSH offers one the server doesn’t know.
  • Permissions too open. Your private key or ~/.ssh directory is readable by other users, so SSH refuses to use it.

Work through them in this order:

flowchart TB A["ssh -T git@github.com
Permission denied (publickey)"] --> B{"Connected as git@?"} B -- No --> F1["Fix 1: use the git user"] B -- Yes --> C{"ssh -vT shows
'Offering public key'?"} C -- No --> F2["Fix 2 and 3: create the key
and load it into ssh-agent"] C -- Yes --> D{"Is that key on
your GitHub account?"} D -- No --> F4["Fix 4: add the public key"] D -- "Yes, but a different key was offered" --> F5["Fix 5: point SSH at the right key"] D -- Yes --> F6["Fix 6: set key file permissions"]

Step 1: Test the SSH Connection

Test authentication without Git in the way:

ssh -T git@github.com

A working setup answers with your username:

Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.

GitLab answers Welcome to GitLab, <username>! when you run ssh -T git@gitlab.com. Any other answer, including Permission denied (publickey)., means authentication failed.

Step 2: Read the Verbose Output

Add -v to see which keys SSH tries:

ssh -vT git@github.com 2>&1 | grep -E "Offering|Authentications that can continue|No more authentication|Permission denied"

This output comes from a key that exists locally but isn’t registered with GitHub:

debug1: Authentications that can continue: publickey
debug1: Offering public key: ./id_ed25519 ED25519 SHA256:+4pg7wFJhqlWjrkXDsmkl/+4cf4GdO/I20y+zX13mmY explicit
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
git@github.com: Permission denied (publickey).

Read it like this:

  • No Offering public key line. SSH had no key to offer. Go to Fix 2 and Fix 3.
  • A key is offered and then rejected. GitHub doesn’t know that key. Compare its fingerprint with the keys on your account (Fix 4), or check that SSH offered the key you meant (Fix 5).

Fix 1: Connect as the git User

GitHub, GitLab, and Bitbucket all expect the user git. Your own username fails, even with a valid key:

jeff@github.com: Permission denied (publickey).

Check the remote URL of your repository:

git remote -v

It should read git@github.com:OWNER/REPO.git. Fix it if it doesn’t:

git remote set-url origin git@github.com:OWNER/REPO.git

Fix 2: Create an SSH Key

List your keys:

ls -l ~/.ssh

If you see no id_ed25519 or id_rsa file, create an Ed25519 key:

ssh-keygen -t ed25519 -C "you@example.com"

Accept the default path, ~/.ssh/id_ed25519, and set a passphrase.

Fix 3: Load the Key Into ssh-agent

Start the agent and list the keys it holds:

eval "$(ssh-agent -s)"
ssh-add -l
The agent has no identities.

Add your key and check again:

ssh-add ~/.ssh/id_ed25519
ssh-add -l
Identity added: /Users/you/.ssh/id_ed25519 (you@example.com)
256 SHA256:... you@example.com (ED25519)

On macOS, store the passphrase in the keychain so the key loads after a reboot:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

Then add this to ~/.ssh/config:

Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

Fix 4: Add Your Public Key to GitHub

Copy the public key, the file ending in .pub. Never paste the private key anywhere.

# macOS
pbcopy < ~/.ssh/id_ed25519.pub

# Linux
cat ~/.ssh/id_ed25519.pub

In GitHub, open Settings, then SSH and GPG keys, choose New SSH key, and paste it. With the GitHub CLI installed, one command does the same thing:

gh ssh-key add ~/.ssh/id_ed25519.pub --title "work laptop"

Run ssh -T git@github.com again. The fingerprint that ssh-add -l prints should match one listed on the GitHub page.

Fix 5: Point SSH at the Right Key

With several keys, SSH offers them in order and stops after too many failures. Tell it which key belongs to which host, and set IdentitiesOnly yes so it offers only that key:

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

To test a specific key without editing the config:

ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes -T git@github.com

Separate work and personal GitHub accounts need a host alias for each account. How to Use Multiple GitHub Accounts on the Same Computer walks through that setup.

Fix 6: Set SSH Key File Permissions

SSH refuses a private key that other users can read. When it loads such a key, for example through ssh-add or when the server accepts the matching public key, it prints this warning and ignores the key:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for './id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

On Linux and macOS

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519      # private key: owner read and write only
chmod 644 ~/.ssh/id_ed25519.pub  # public key: readable by everyone
chmod 600 ~/.ssh/config

Check the result and confirm that you own the files:

ls -ld ~/.ssh
ls -l ~/.ssh

Fix ownership if another user owns them:

sudo chown -R "$USER" ~/.ssh

On Windows

Windows OpenSSH checks file access lists instead of Unix modes. In PowerShell, remove inherited access and grant read access to your account only:

icacls "$env:USERPROFILE\.ssh\id_ed25519" /inheritance:r
icacls "$env:USERPROFILE\.ssh\id_ed25519" /grant:r "${env:USERNAME}:(R)"

Check the result:

icacls "$env:USERPROFILE\.ssh\id_ed25519"

Only your account (plus SYSTEM and Administrators, if they appear) should have access.

On a Server You Run

When the server is your own Linux machine, the server-side files matter too. With the default StrictModes yes, sshd ignores authorized_keys if the files are writable by other users:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod go-w ~

Confirm your public key is in ~/.ssh/authorized_keys, then read the server log for the real reason:

sudo journalctl -u ssh -u sshd --since "10 minutes ago"

A line such as Authentication refused: bad ownership or modes for directory /home/you points straight at the permission to fix.

Checklist

  • Test with ssh -T git@github.com before you debug Git.
  • Use the git user in the remote URL.
  • Confirm ssh-add -l lists your key.
  • Confirm the key’s fingerprint appears in your GitHub SSH keys.
  • Pin the key with IdentityFile and IdentitiesOnly yes when you have several.
  • Keep private keys at 600 and ~/.ssh at 700.

References