How to add an SSH key to GitHub, GitLab and Bitbucket

With an SSH key, git push and pull stop asking for tokens. Add a key to GitHub, GitLab or Bitbucket, use several accounts on one machine, and sign commits with the same key.

updated

Quick answer. Create a key, paste the contents of ~/.ssh/id_ed25519.pub into GitHub under Settings, SSH and GPG keys, New SSH key, then test it. The test should greet you by your GitHub username.

ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub
ssh -T [email protected]

SSH or HTTPS for git?

Git talks to GitHub, GitLab and Bitbucket over HTTPS or SSH. GitHub stopped accepting account passwords for git operations in 2021, so HTTPS now means a personal access token or a credential helper. SSH means a key pair: you add the public key to your account once, and every git clone, pull and push using a [email protected]: address authenticates with it.

The key itself is created on your computer; the guides for macOS, Linux and Windows cover the platform details such as the agent. This page is about the git host side.

Add an SSH key to GitHub

  1. Create a key, or reuse one you have

    One Ed25519 key per computer is the usual setup, and the same key can be added to GitHub, GitLab and Bitbucket. Generate one with ssh-keygen, or in your browser with the sshkeygen.dev generator.

    ssh-keygen -t ed25519 -C "[email protected]"
  2. Copy the public key

    Copy the contents of the .pub file, one line starting with ssh-ed25519. Never paste the private key, the file without an extension.

    pbcopy < ~/.ssh/id_ed25519.pub                                    # macOS
    cat ~/.ssh/id_ed25519.pub                                         # Linux, then select
    Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard  # Windows
  3. Add the key in GitHub settings

    On GitHub, open your profile picture menu, then Settings, SSH and GPG keys, New SSH key (or go straight to github.com/settings/keys). Give it a title that names the computer, leave the key type as Authentication Key, paste the key and click Add SSH key.

  4. Test the connection

    The first time, ssh asks you to confirm GitHub's host key. Compare the fingerprint with the one GitHub publishes (for Ed25519 it is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU) and type yes.

    ssh -T [email protected]

    A successful test answers Hi username! You've successfully authenticated, but GitHub does not provide shell access.

  5. Use SSH URLs for your repositories

    Clone with the SSH address, or switch an existing clone from HTTPS:

    git clone [email protected]:owner/repo.git
    git remote set-url origin [email protected]:owner/repo.git

If you use the GitHub CLI, gh ssh-key add ~/.ssh/id_ed25519.pub --title "work laptop" does step 3 from the terminal.

Verify GitHub's host key on first connect

The first test prints something like this:

$ ssh -T [email protected]
The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.

The question protects you from a network that impersonates GitHub. Answer yes only if the fingerprint matches one GitHub publishes in its documentation:

HostTypeFingerprint
github.comED25519SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU
github.comECDSASHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM
github.comRSASHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s
gitlab.comED25519SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8
bitbucket.orgED25519SHA256:ybgmFkzwOSotHTHLJgHO0QN8L0xErw6vd0VhFA9m3SM

Which type ssh shows depends on your client; any of them matching is fine. If the fingerprint differs, stop and find out why before typing yes. Background in SSH key fingerprints.

Switch a repository from HTTPS to SSH

A repository cloned over HTTPS keeps asking for credentials. Check its remote:

$ git remote -v
origin  https://github.com/owner/repo.git (fetch)
origin  https://github.com/owner/repo.git (push)

Replace the address with the SSH form and check again:

git remote set-url origin [email protected]:owner/repo.git
git remote -v
git fetch

The pattern is the same on every host: https://HOST/PATH.git becomes git@HOST:PATH.git. To make git rewrite every GitHub HTTPS address automatically, add git config --global url."[email protected]:".insteadOf "https://github.com/".

GitLab

  1. Sign in, open your avatar menu and choose Edit profile, then SSH Keys in the sidebar.
  2. Click Add new key, paste the public key, give it a title, and optionally set an expiration date. GitLab emails you before a key expires.
  3. Test it. The answer is Welcome to GitLab, @username!
ssh -T [email protected]

On a self-managed GitLab, use your instance's host name, and get its host key fingerprints from your administrator. GitLab can also mark a key for authentication, signing, or both.

Bitbucket

  1. Open the gear menu, choose Personal Bitbucket settings, then SSH keys.
  2. Click Add key, give it a label, paste the public key and save.
  3. Test it. Bitbucket replies that you are authenticated and that shell access is disabled.
ssh -T [email protected]

Deploy keys for servers and CI

For a key only one repository should use, such as on a build server, add it as an access key (Bitbucket) or deploy key (GitHub, GitLab) in that repository instead. It can be read-only.

  • Create a dedicated key without a passphrase on the machine that needs it: ssh-keygen -t ed25519 -N "" -f ~/.ssh/deploy_repo -C "deploy@build-01".
  • Add the public key in the repository's settings, under Deploy keys, and allow write access only if the machine has to push.
  • On GitHub, a deploy key can be attached to a single repository. For several repositories, use one key per repository with host aliases, or a machine user.

A deploy key gives access to one repository instead of everything your account can reach, so a leaked build server costs much less.

Several accounts on one computer

The host identifies the account by the key, so each account needs its own key and a host alias in ~/.ssh/config:

ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_work
Host github.com
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

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

Clone work repositories with the alias, as in git clone git@github-work:company/repo.git. IdentitiesOnly yes is essential: without it, ssh offers the agent's first key and GitHub logs you in as that account. Test each alias separately; each should greet a different username:

ssh -T [email protected]
ssh -T git@github-work

For an existing work clone, point its remote at the alias with git remote set-url origin git@github-work:company/repo.git, and set the matching commit email in that repository with git config user.email [email protected].

Signing commits with your SSH key

Git 2.34 and later sign commits with an SSH key, no GPG needed. Add the public key on GitHub again with the type Signing Key (on GitLab, usage set to signing), then:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true

GitHub shows a Verified badge when the signing key is on your account and the commit's email is a verified address of that account. To verify signatures locally as well, tell git which keys to trust with an allowed_signers file:

mkdir -p ~/.config/git
echo "[email protected] namespaces=\"git\" $(cat ~/.ssh/id_ed25519.pub)" >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
git log --show-signature -1

The last command should print Good "git" signature for [email protected] with ED25519 key SHA256:.... The signing itself is done by ssh-keygen -Y sign; see the ssh-keygen reference.

SSH over port 443

Some corporate and hotel networks block outgoing port 22. All three hosts also answer SSH on port 443, under a different host name. Add this to ~/.ssh/config:

Host github.com
  HostName ssh.github.com
  Port 443
  User git

Host gitlab.com
  HostName altssh.gitlab.com
  Port 443
  User git

Host bitbucket.org
  HostName altssh.bitbucket.org
  Port 443
  User git

Test with ssh -T [email protected] as before. Your repository URLs do not change.

Troubleshooting

"[email protected]: Permission denied (publickey)"

Run ssh -vT [email protected] and check which files are offered. Usually the key is not loaded (ssh-add -l), has a non-default name without an IdentityFile line, or belongs to another account. The same message from git push also appears if you ran git with sudo, which uses root's keys.

"Key is already in use"

A key can belong to only one GitHub account, or to one repository as a deploy key. Use a separate key for the second account.

"ERROR: Repository not found" or "Please make sure you have the correct access rights"

You authenticated, but as an account without access to that repository. ssh -T [email protected] shows which username the key belongs to. Fix the alias or IdentityFile so the right key is used.

Git still asks for a username and password

The repository uses an HTTPS remote. Check it with git remote -v and switch it to the [email protected]: form with git remote set-url.

"Host key verification failed"

You answered no to the host key question, or known_hosts holds an old key. GitHub rotated its RSA host key in 2023, which produced this error for many people. Remove the stale entry with ssh-keygen -R github.com and connect again, checking the fingerprint against the table above.

"sign_and_send_pubkey: signing failed ... agent refused operation"

The agent holds a key it cannot use. Clear it with ssh-add -D, make sure the private key has mode 600, and add it again.

Port 22 is blocked by a firewall

ssh: connect to host github.com port 22: Connection timed out means the network blocks SSH. Use the port 443 configuration above.

FAQ

Can I use the same SSH key for GitHub, GitLab and Bitbucket?

Yes. A public key can be added to any number of services, and each one only learns your public key. The usual setup is one key per computer, added to every host you use from that computer.

Can one SSH key belong to two GitHub accounts?

No. GitHub identifies the account by the key, so a key can be attached to only one account, and adding it again gives "Key is already in use". Create a second key and a host alias in ~/.ssh/config, as shown above.

Why does ssh -T [email protected] exit with an error code when it works?

GitHub closes the session right after the greeting because it does not provide a shell, so ssh exits with status 1. The "Hi username!" message is what tells you authentication succeeded.

Should I use SSH or HTTPS with GitHub?

Both are secure. SSH uses a key you set up once and works well with an agent; HTTPS uses a personal access token or a credential manager and passes through proxies that block port 22. If you already have an SSH key, SSH is the simpler daily setup.

Do SSH keys on GitHub expire?

GitHub keys have no expiry date, but GitHub removes a key that has not been used for a year as a precaution. GitLab lets you set an expiry date per key. Review your keys now and then and delete ones you do not recognise.

What is the difference between an authentication key and a signing key on GitHub?

An authentication key lets you push and pull over SSH. A signing key is used only to verify signatures on commits and tags. The same public key can be added twice, once of each type.

Does GitHub accept RSA keys?

Yes, RSA keys still work, signed with SHA-2 by any current OpenSSH. DSA keys are no longer accepted. For a new key, Ed25519 is the better choice; see Ed25519 vs RSA.