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 -Twith the alias and read the name. - Which key is really used.
ssh -vT git@github-workprints 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
- Managing multiple accounts, GitHub Docs
- Error: Key already in use, GitHub Docs
- git-config, conditional includes
- ssh_config(5), OpenBSD manual