Mendhak / Code

Managing multiple SSH keys for multiple GitHub organisations in a simple way

When working with multiple GitHub organisations, it is common to have to manage multiple SSH keys for git operations.

The following solution is the one I have found to be the most convenient, with the least amount of overhead or behavioral changes, and as close to seamless as possible.

Suppose you are in two orgs, org_1 and org_2, and you have registered two SSH keys id_ed25519_org_1 and id_ed25519_org_2 for those orgs.

First, create a configuration file for each org.

For org_1, create ~/.gitconfig_org_1 with an SSH command that uses the key for that org. Replace the path to the SSH key with yours.

[core]
    sshCommand = "ssh -i /home/ubuntu/.ssh/id_ed25519_org_1 -F /dev/null"

Similarly, for org_2, create ~/.gitconfig_org_2 with the key path for that org.

[core]
    sshCommand = "ssh -i /home/ubuntu/.ssh/id_ed25519_org_2 -F /dev/null"

Now, edit your main ~/.gitconfig file to include those org-specific files by adding the following lines. Replace org_1 and org_2 with the names of your Github organisations.

[includeIf "hasconfig:remote.*.url:[email protected]:org_1/**"]
        path = ~/.gitconfig_org_1
[includeIf "hasconfig:remote.*.url:[email protected]:org_2/**"]
        path = ~/.gitconfig_org_2

That’s it. You can now do a git clone against a repo, and the correct SSH key will be used. The output should look something like this:

$ git clone [email protected]:org_1/my_repo.git
Cloning into 'my_repo'...
Enter passphrase for key '/home/mendhak/.ssh/id_ed25519_org_1':
remote: Enumerating objects...
...

Similarly when cloning a repo in org_2, git will use the correct key.

How it works

The includeIf section in .gitconfig allows conditionally including configuration from another file. There are different kinds of conditions, and the hasconfig:remote is what’s being used here. The fragment will match on the remote URL of the repository.

The reason it works is because for repos in org_1, the git clone URL will include the name of the org, org_1: [email protected]:org_1/my_repo.git. An org_2 repo will have a URL like [email protected]:org_2/my_repo.git.

By matching on these fragments, we include different configuration files. Those configuration files in turn set the sshCommand to make use of the correct SSH keys.

Solutions I didn’t like

In my research, these were the most common solutions as suggested on the internet and various mediocre LLM responses.

Modifying the SSH config

This is the most common solution I see, which is to use multiple Host entries that all point at github.com, but with different keys.

Host github_org1
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_org_1
    IdentitiesOnly yes
Host github_org2
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_org_2
    IdentitiesOnly yes

This isn’t great, because you have to change the git clone URL whenever you’re cloning: git clone git@github_org1:my_repo.git, where the github.com has been replaced with github_org1.

Matching on directories

Another common solution is to match on the directory name. Here you clone repos into different directories for each org. It’s somewhat similar to the main one above.

[includeIf "gitdir:~/org_1/**"]
        path = ~/.gitconfig_org_1
[includeIf "gitdir:~/org_2/**"]
        path = ~/.gitconfig_org_2

Not terrible, but the downside is that you have to clone into a specific destination, and that isn’t very intuitive or flexible.

Helper scripts to switch keys

No.