How to use multiple GitHub accounts with SSH

GitHub identifies your account from the SSH key, so use one key per account. In ~/.ssh/config, create a Host alias for each with HostName github.com, its own IdentityFile and IdentitiesOnly yes. Then clone with the alias, as in git clone git@github-work:org/repo.git. Use includeIf in Git to set the right email per folder.

Updated . Config, Git and agent behavior tested with OpenSSH and Git 2.47 against a local SSH server set up to act like GitHub: one git user, with each key mapped to a different account name, including real clone and push.

Why this needs any setup

Every GitHub connection goes to the same host with the same user, git. GitHub tells accounts apart only by the key you present, and ssh stops at the first key the server accepts. So you need a way to say which key to use for which repository. Host aliases are the usual way.

1. One key per account

ssh-keygen -t ed25519 -C "personal" -f ~/.ssh/id_ed25519_personal
ssh-keygen -t ed25519 -C "work" -f ~/.ssh/id_ed25519_work

Add each .pub file to its account, as in adding a key to GitHub.

2. An alias for each in ~/.ssh/config

Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_personal
    IdentitiesOnly yes

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

Check what an alias resolves to, without connecting:

ssh -G github-work | grep -E '^(hostname|user|identityfile|identitiesonly) '

Then test each one. It should greet you with the right username:

ssh -T git@github-personal
ssh -T git@github-work

Why IdentitiesOnly matters

Without it, ssh also tries every key in your agent. In our test, the key listed in the config was tried first. But when that key was refused, ssh went on to another key from the agent and logged in as the other account, with no error. That is how a push ends up under the wrong name, or a repository seems to be missing. With IdentitiesOnly yes, the same test failed with Permission denied, which is what you want. It also prevents Too many authentication failures when your agent holds many keys.

3. Use the alias in the address

Replace github.com with the alias:

git clone git@github-work:org/repo.git

For a repository you already cloned:

git remote set-url origin git@github-work:org/repo.git

To avoid changing addresses you copy from GitHub, Git can rewrite them for one organization:

git config --global url."git@github-work:org/".insteadOf "git@github.com:org/"

4. The right name on your commits

The SSH key decides who pushes. Your commits carry whatever user.email says, which is a separate setting. Keep work repositories in one folder and add this to ~/.gitconfig:

[user]
    name = Your Name
    email = you@example.com

[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

And in ~/.gitconfig-work:

[user]
    email = you@work.example.com

Keep the trailing slash in gitdir:~/work/, which is what makes it match everything inside. Check it from inside a repository:

cd ~/work/some-repo
git config user.email

Run this inside a repository: in our test, the plain folder ~/work still showed the global email, because the condition depends on the repository's location.

Alternative: choose the key per folder, without aliases

Put the key in the same include file, and keep the normal github.com addresses:

[core]
    sshCommand = ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes

It applies only once Git is inside a repository in that folder. So the first clone needs the key set by hand, which is also GitHub's documented one-off method:

GIT_SSH_COMMAND='ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes' git clone git@github.com:org/repo.git

If you would rather match on the repository's address than on its folder, Git 2.36 and later also accept conditions such as [includeIf "hasconfig:remote.*.url:git@github-work:*/**"].

If it goes wrong

  • "Repository not found" with the right address. You are probably authenticated as the other account. Run ssh -T with the alias and read the name.
  • Which key is really used. ssh -vT git@github-work prints each key it offers.
  • Permission denied. See Permission denied (publickey) with Git.

Questions

Can I use one key for both accounts?

No. GitHub refuses a key that is already on another account, with the error Key already in use.

Do I still need the alias if I use core.sshCommand?

No. Either one is enough to select the key. The alias is visible in the remote address, and core.sshCommand keeps the normal github.com address, which is convenient for scripts that expect it.

Sources

More in Configuration and many servers

All guides