DEV Community

Svyatoslav Pavlov
Svyatoslav Pavlov

Posted on Originally published at termal.in on

GitHub / GitLab SSH key not working: the fix, step by step

An SSH key that won't authenticate to GitHub or GitLab is one of the most-Googled developer problems there is, and half the time the key is fine — the diagnosis is just wrong. So before you regenerate anything, run one test and learn to read its answer. Then work the culprits in order of how often they're actually the cause.

Step 0: the test that tells you if it's even broken

Ask the git host directly whether it recognizes your key:

ssh -T git@github.com        # GitHub
ssh -T git@gitlab.com        # GitLab
Enter fullscreen mode Exit fullscreen mode

Now read the response carefully, because the success message looks like an error:

Hi <username>! You've successfully authenticated, but GitHub does not
provide shell access.
Enter fullscreen mode Exit fullscreen mode

That is success. "Does not provide shell access" is not a failure — GitHub and GitLab don't give you a shell, they only speak git over SSH, so they authenticate you and then hang up. People see "does not provide" and start troubleshooting a working setup. If you see your username in that line, your key works; your problem is somewhere else (jump to the HTTPS remote culprit below — that's the usual real issue).

The genuine failure looks like this instead:

git@github.com: Permission denied (publickey).
Enter fullscreen mode Exit fullscreen mode

That's the one we're here to fix. Note the endpoint: Permission denied (publickey) means the server accepted no key you offered. Work down the list.

Culprit 1: the key isn't added to your account

The most common real cause. A key on your laptop does nothing until its public half is registered with the git host. Copy the public key:

cat ~/.ssh/id_ed25519.pub     # note the .pub — this is the safe half to share
Enter fullscreen mode Exit fullscreen mode

Paste that into GitHub → Settings → SSH and GPG keys → New SSH key, or GitLab → Preferences → SSH Keys.

One rule, no exceptions: paste the .pub file, never the file without .pub. The public key is one line starting ssh-ed25519 AAAA… (or ssh-rsa AAAA…). If what you're about to paste starts with -----BEGIN OPENSSH PRIVATE KEY-----, stop — that's your private key, the secret that is your identity. Pasting it into a web form hands your credential to anyone who can read that page. Share the public half; guard the private half.

(No key yet? Generate one — Ed25519 in almost all cases: ssh-keygen -t ed25519 -C "you@device".)

Culprit 2: the wrong key is being offered

Your key is registered, but SSH is offering a different one — common when you carry several keys and the agent tries them in an order the server rejects. See exactly which key is sent:

ssh -vT git@github.com
Enter fullscreen mode Exit fullscreen mode

Look for the Offering public key: lines. If the key you registered isn't among them, or a wrong one is being accepted, pin the right key per host in ~/.ssh/config:

Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
Enter fullscreen mode Exit fullscreen mode

IdentitiesOnly yes is the key line — it tells SSH to offer only the IdentityFile you named and stop spraying every key in your agent at the server (which also cures Too many authentication failures). Do the same with Host gitlab.com for GitLab. More on structuring the file in the ssh_config guide.

Culprit 3: the agent isn't running, or the key isn't loaded

If your key has a passphrase and the agent doesn't hold it, SSH may skip it silently. Check what the agent has:

ssh-add -l
Enter fullscreen mode Exit fullscreen mode

The agent has no identities (or Could not open a connection to your authentication agent) means you need to start it and load the key:

eval "$(ssh-agent -s)"        # start the agent in this shell
ssh-add ~/.ssh/id_ed25519     # load the key (prompts for the passphrase once)
Enter fullscreen mode Exit fullscreen mode

ssh-add -l should now list your key's fingerprint. (For what the agent actually is and how it holds keys, see what is the SSH agent.)

Culprit 4: you're on an HTTPS remote, not an SSH one

This is the culprit behind "my key works (ssh -T succeeded) but git push still asks for a password." Your repository is configured to use HTTPS, which ignores SSH keys entirely and wants a token. Check:

git remote -v
Enter fullscreen mode Exit fullscreen mode

If you see https://github.com/you/repo.git, that's the problem — HTTPS never touches your SSH key. Switch the remote to SSH:

git remote set-url origin git@github.com:you/repo.git      # GitHub
git remote set-url origin git@gitlab.com:you/repo.git      # GitLab
Enter fullscreen mode Exit fullscreen mode

Run git remote -v again to confirm it now starts with git@. This single fix resolves a huge share of "SSH key not working" reports where the key was fine all along.

Culprit 5: permissions on ~/.ssh

SSH refuses to use keys and config that others could tamper with. If verbose output mentions bad modes, or keys are being ignored for no clear reason:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519          # private key
chmod 644 ~/.ssh/id_ed25519.pub      # public key
chmod 600 ~/.ssh/config
Enter fullscreen mode Exit fullscreen mode

On Windows this surfaces after copying keys off a synced drive or between machines; fix it via the file's Security properties, or keep keys out of cloud-synced folders.

Culprit 6: passphrase prompts every time

If every git operation demands your key passphrase, the agent isn't caching it. Load it once with ssh-add (culprit 3), and make it stick across sessions by adding to ~/.ssh/config:

Host github.com gitlab.com
    AddKeysToAgent yes
Enter fullscreen mode Exit fullscreen mode

On macOS, ssh-add --apple-use-keychain ~/.ssh/id_ed25519 stores the passphrase in Keychain so it survives reboots.

Culprit 7: two accounts, one host

Work and personal GitHub accounts can't share one key — a given public key belongs to exactly one account. Give each its own key and a Host alias that selects it:

Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/work_ed25519
    IdentitiesOnly yes

Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/personal_ed25519
    IdentitiesOnly yes
Enter fullscreen mode Exit fullscreen mode

Then clone or set the remote with the alias as the host: git@github-work:org/repo.git. SSH matches github-work, picks the work key, and rewrites the connection to github.com under the hood.

Culprit 8: organization SSO not authorized (GitHub)

You authenticate fine to your own repos but get Permission denied on an org's repos behind SAML SSO. The key exists but isn't authorized for that organization. Go to Settings → SSH and GPG keys, find the key, click Configure SSO, and authorize it for the organization. GitLab has a parallel case where an expired key is silently rejected — check the key's expiry date in SSH Keys if a previously working key suddenly stops.

When it's still Permission denied (publickey)

If you've worked the list and still hit the endpoint, you've narrowed it to a genuine key-acceptance problem — the systematic loop for that exact error (reading ssh -vvv, the server's method list, offered vs. accepted keys) is in permission denied (publickey), a fast systematic fix, and the broader map of SSH errors is in SSH errors decoded. But for git hosts specifically, the order above catches nearly everything: register the public key, offer the right one, load it into the agent, and make sure you're actually on an SSH remote.

Where a client removes the setup entirely

Most of this friction is bookkeeping — which key belongs to which host, whether it's loaded, whether the remote is even SSH. Termalin keeps the key with the host it belongs to, unlocks your keys once through a built-in agent so passphrases aren't retyped (and a cancelled prompt never silently skips a key), and — because it speaks the standard ssh-agent protocol — git and your CLI can sign through that same agent. Fingerprints are visible per key, so registering the right public half with GitHub or GitLab is copy-and-paste rather than a hunt through five ~/.ssh folders. The account-and-alias juggling above becomes a per-host setting instead of a config-file puzzle.


Termalin is a free, cross-platform SSH client that manages your keys with a one-unlock agent git can sign through — download it, or read how it handles keys safely.

Top comments (0)