Using Multiple GitHub Accounts on a Single Machine with Nix and Home Manager

There’s reasonable documentation available for how to manage multiple GitHub accounts on a single machine. This can be helpful if you have one account for personal use and another for your work. I came across another guide that made some improvements over GitHub’s basic instructions but, since I’m a special snowflake, I still couldn’t get quite the setup I wanted.

Here are my goals

  1. Ensure commits to personal repositories use my personal account and commits to work repositories use my work account.
  2. Use SSH, not HTTPS to interact with GitHub.
  3. Use separate SSH keys for personal and work accounts.
  4. Let me use standard git commands for everything. I don’t want to have to get used to a wrapper script or manually set environment variables.
  5. Manage all of this configuration using the Nix package manager and Home Manager on macOS.

Setting up Nix and Home Manager are beyond the scope of this article. I’ll assume you have them already up and running. I also don’t use Nix Flakes, so if that’s your thing, best of luck!

Now, on to making it work.

Configure SSH

We’ll need to configure SSH a bit, so add this to your home.nix:

programs.ssh = {
  enable = true;

  "*" = {
    extraOptions = {
      AddKeysToAgent = "yes";
      UseKeychain = "yes";
      IdentitiesOnly = "yes";
  };
};

Create separate personal and work SSH keys

Next, create the keys

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

Be sure to use a nice strong (different!) password for each one when prompted. Don’t worry, we’re going to store it in the keychain so you won’t have to type this very often.

Ensure ssh-agent is running

eval "$(ssh-agent -s)"

Now, add the keys. This is one of the rare times you’ll be prompted for those passwords you used for your keys.

ssh-add --apple-use-keychain ~/.ssh/id_ed25519_personal
ssh-add --apple-use-keychain ~/.ssh/id_ed25519_work

OK, now we need to tell GitHub about these keys. First, grab the public key for your personal SSH key

pbcopy < ~/.ssh/ed25519_personal.pub

Then, log into your personal GitHub account and add this new SSH key to your account.

Repeat for your work account

pbcopy < ~/.ssh/ed_25519_work.pub

Configure git

With our SSH keys in place, we now need to configure git to use the right key at the right time. We also need to use the right email address when authoring commits. In my case, all of my work repositories live under a particular GitHub Organization, so I’m going to set up a rule to use my work SSH key and work email address for all repositories in that organization. For all repositories outside that organization, I’ll use my personal SSH key and email address. To do this, we’ll need to

  1. Modify the SSH command git invokes to use the right SSH key
  2. Modify git’s user.email config to use the right email address

We’ll accomplish this by configuring our personal SSH key and email address as the defaults, then overriding them when we’re in a work repository. Here’s our git configuration in Home Manager

programs.git = {
  enable = true;
  userName = "Your Name";
  userEmail = "personal@example.com";

  extraConfig = {
    core = {
      sshCommand = "ssh -i ~/.ssh/id_ed25519_personal";
    };
  };

  includes = [
    {
      contents = {
        user = {
	  email = "work@example.com";
	};

	core = {
	  sshCommand = "ssh -i ~/.ssh/id_ed25519_work";
        };
      };

      condition = "hasconfig:remote.*.url:git@github.com:WORK_ORGANIZATION/*";
    }
  ];
};

The trick here is in the includes section. Here, we specify an SSH command and email address to use instead of the default personal values. However, these work values are only used if the current repository has a git remote in WORK_ORGANIZATION. There are other conditions we could use. For example, if you keep your work repositories in a specific directory on your machine, like ~/work/ then you could use condition = "gitdir:~/work/";. In fact, that’s probably a better approach, since it won’t require you to jump through any hoops when initially cloning a repository from your work organization.