Multiple GitHub Accounts

Choosing an approach

Having two GitHub accounts on one machine is easy. Keeping them from bleeding into each other is not: by default a single global identity, a single SSH agent, and a single credential store serve every repository you touch. A work commit can end up authored with your personal email, signed with your personal key, or pushed through your personal token without any warning.

This guide assumes you already have one account working as described in Connect GitHub SSH. It uses personal and work as placeholder account names - substitute your real usernames throughout.

There are two ways to separate the accounts:

Separate folders, one Windows user Separate Windows users
How it works Each account owns a folder; Git, SSH, and GitHub CLI pick the identity from the folder you are in Each account gets its own Windows login with its own profile
Switching cd into the other folder Switch Windows user
GitHub Desktop One signed-in account at a time One signed-in account per Windows user, both at once
Isolation Enforced by configuration - a mistake in the config can leak Enforced by the operating system - nothing is shared by default

If "zero trace" is a hard requirement - an employer's repositories, for example - use separate Windows users. If you want convenience and can accept that correctness depends on your config, use separate folders. Both are described below.

Where leaks come from

Every one of these is a real default on a freshly configured machine:

Source Default behavior What leaks
~/.gitconfig One user.email and user.signingkey for every repository Work commits carry your personal email and personal signature
Signing key Registered on one account Anyone can match the key to its owner via github.com/<username>.keys
SSH agent Offers every loaded key to GitHub, in order GitHub sees your other public key, and may authenticate you as the wrong account
Credential Manager Stores one git:https://github.com login Every HTTPS push authenticates as that account
GitHub CLI One active account for the whole machine gh pr create in a work repository runs as your personal account
GitHub Desktop One signed-in github.com account Pull requests, checks, and clone lists use that account's token
Browser Whatever account is logged in gh auth login --web and Desktop's sign-in authorize that account

Option A: separate folders, one Windows user

Pick one root folder per account, for example D:\github-personal\ and D:\github-work\. Every repository of an account lives under its folder. Anything outside both folders gets no identity at all, so a forgotten repository fails loudly instead of silently using the wrong account.

Generating a key per account

  1. Rename your existing key so it no longer uses the default file name. SSH tries id_ed25519 automatically whenever no key is specified, which would defeat the fail-closed default:
mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519_personal
mv ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519_personal.pub
  1. Generate a key for the work account:
ssh-keygen -t ed25519 -C "work_email@example.com" -f ~/.ssh/id_ed25519_work
  1. Add it to the native Windows agent from a standard PowerShell window. The personal key stays loaded - the agent keeps its own copy, so the rename does not affect it:
ssh-add C:\Users\YourUsername\.ssh\id_ed25519_work
  1. Upload id_ed25519_work.pub to the work account twice - once as an Authentication Key and once as a Signing Key. Never register the same key on both accounts.

Splitting the Git config

The global file keeps only what both accounts share and hands identity to a per-folder file through includeIf.

  1. Replace the contents of ~/.gitconfig with the following, keeping any unrelated sections you already have (such as [filter "lfs"]):
[user]
	useConfigOnly = true
[core]
	sshCommand = C:/Windows/System32/OpenSSH/ssh.exe -o IdentitiesOnly=yes
[url "git@github.com:"]
	insteadOf = https://github.com/
[gpg]
	format = ssh
[gpg "ssh"]
	program = C:/Windows/System32/OpenSSH/ssh-keygen.exe
	allowedSignersFile = C:/Users/YourUsername/.ssh/allowed_signers

[includeIf "gitdir/i:D:/github-personal/"]
	path = ~/.gitconfig-personal
[includeIf "gitdir/i:D:/github-work/"]
	path = ~/.gitconfig-work
  1. Create ~/.gitconfig-personal:
[user]
	name = Your Name
	email = personal_email@example.com
	signingkey = C:/Users/YourUsername/.ssh/id_ed25519_personal.pub
[commit]
	gpgsign = true
[core]
	sshCommand = C:/Windows/System32/OpenSSH/ssh.exe -i C:/Users/YourUsername/.ssh/id_ed25519_personal -o IdentitiesOnly=yes
  1. Create ~/.gitconfig-work with the same shape, using the work email (or the work account's @users.noreply.github.com address) and the id_ed25519_work paths.

What each piece does:

  • user.useConfigOnly = true - with no email set globally, Git refuses to commit outside the two folders (Author identity unknown) instead of guessing.
  • IdentitiesOnly=yes - SSH offers only the key named with -i, even though the agent holds both. GitHub never sees the other account's public key. The global sshCommand has no -i, and since no key uses a default file name, pushes outside both folders fail.
  • insteadOf - every https://github.com/ remote is rewritten to SSH, so the credential stored in Windows Credential Manager is never consulted for GitHub. This also covers GitHub Desktop and GitHub CLI, which both call Git.
  • includeIf "gitdir/i:..." - the /i makes the match case-insensitive, which matters on Windows. The trailing slash matches everything beneath the folder. The includes sit last so they override the defaults above them.

The per-folder config already applies during git clone. Cloning into D:\github-work\ uses the work key from the first connection, so there is no need for SSH host aliases or -c flags. Remotes stay plain git@github.com:owner/repo.git, which GitHub Desktop and GitHub CLI both recognize as GitHub.

Never put a ; or # in an unquoted sshCommand. Both start a comment in Git config files and silently truncate the command.

Trusting both signing keys

Add a line for the work key to the allowed signers file so git log --show-signature verifies commits from both accounts:

printf '%s %s\n' "work_email@example.com" "$(cat ~/.ssh/id_ed25519_work.pub)" >> ~/.ssh/allowed_signers

The existing line for the personal key keeps working after the rename - the file stores the key itself, not its path.

Separating GitHub CLI

GitHub CLI supports several accounts (gh auth switch), but the active account is machine-wide, so forgetting to switch is a leak. Give each account its own config directory instead, and select it automatically from the current folder.

  1. Set the personal account (in the default config directory) to use SSH:
gh config set git_protocol ssh
  1. Log in to the work account into its own directory. Do this in a browser window where the work account is signed in:
GH_CONFIG_DIR=~/.config/gh-work gh auth login --web --git-protocol ssh --skip-ssh-key
  1. For Git Bash, add this to ~/.bashrc:
__gh_profile() {
  case "${PWD,,}" in
    /d/github-work|/d/github-work/*) export GH_CONFIG_DIR="$HOME/.config/gh-work" ;;
    /d/github-personal|/d/github-personal/*) unset GH_CONFIG_DIR ;;
    *) export GH_CONFIG_DIR="$HOME/.config/gh-none" ;;
  esac
}
PROMPT_COMMAND="__gh_profile${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
  1. For PowerShell, add this to $PROFILE:
function Set-GhProfile {
  $p = (Get-Location).Path
  if     ($p -like 'D:\github-work*')     { $env:GH_CONFIG_DIR = "$HOME\.config\gh-work" }
  elseif ($p -like 'D:\github-personal*') { Remove-Item Env:GH_CONFIG_DIR -ErrorAction Ignore }
  else                                    { $env:GH_CONFIG_DIR = "$HOME\.config\gh-none" }
}
$__prompt = $function:prompt; function prompt { Set-GhProfile; & $__prompt }

Outside both folders gh points at an empty directory and reports that you are not logged in - the same fail-closed default as Git.

Never run gh auth setup-git. It registers GitHub CLI as a Git credential helper, which hands the active account's token to every Git operation and bypasses the per-folder SSH keys.

GitHub Desktop

At the time of writing, GitHub Desktop signs in to only one github.com account at a time. With the setup above, that matters less than it seems:

  • Desktop runs Git with your global config, so commit author, signing, and SSH pushes all follow the per-folder rules regardless of which account Desktop is signed in to.
  • The signed-in account is only used for GitHub features: the clone list, pull request and checks status, avatars.

Either sign out of Desktop entirely and use it purely as a Git client, or switch accounts in File > Options > Accounts when you change context. If you need both accounts signed in at once, use Option B.

Verifying the setup

From inside a work repository:

  1. Confirm the identity comes from the work file:
git config --show-origin user.email
  1. Confirm the work key authenticates as the work account:
ssh -i ~/.ssh/id_ed25519_work -o IdentitiesOnly=yes -T git@github.com

The response should read Hi work! You've successfully authenticated....

  1. Confirm GitHub CLI only knows the work account:
gh auth status
  1. From a folder outside both roots, confirm git commit fails with Author identity unknown.

Option B: separate Windows users

A second Windows login gives the work account its own profile, and with it everything Git, SSH, and GitHub tools store per user. Isolation no longer depends on your configuration being correct.

What is and is not shared

Item Location Visible to another Windows user?
~/.gitconfig, SSH keys, allowed_signers C:\Users\YourUsername No - only you, Administrators, and SYSTEM can read your profile
Keys loaded in ssh-agent Per-user, encrypted with your Windows login No - the service is machine-wide, but each user's keys are separate
Credential Manager (Git, gh, Desktop tokens) Per-user vault No
GitHub CLI config %APPDATA%\GitHub CLI No
System Git config C:\Program Files\Git\etc\gitconfig Yes - check it contains no user.* or core.sshCommand entries
Environment variables Machine scope Yes - check for GH_TOKEN, GITHUB_TOKEN, or GIT_* entries
Programs installed for all users C:\Program Files Yes - Git for Windows and GitHub CLI install here
Programs installed per user %LOCALAPPDATA% No - GitHub Desktop installs here and must be installed again
Other drives D:\ and so on Yes - Authenticated Users can modify them by default

To check the machine-wide items from PowerShell:

git config --system --list --show-origin
[Environment]::GetEnvironmentVariables('Machine')

Setting up the work user

  1. Create a new local Windows account, and make it a Standard user, not an Administrator. An administrator can take ownership of your personal profile and read your .ssh folder; a standard user cannot.
  2. Do not sign it in with the same Microsoft account as your personal user - that syncs settings, browser profiles, saved passwords, and OneDrive between them.
  3. Install the per-user programs again, starting with GitHub Desktop.
  4. Open a browser with a fresh profile and do not sign in to the browser itself. Signing in syncs saved passwords, including your personal GitHub login.
  5. Follow Connect GitHub SSH from the beginning inside the work user, using the work account's email.
  6. Log in to GitHub CLI and GitHub Desktop as the work account.

Locking the shared drive

Without this step, the work user can read and modify your personal repositories on a shared drive. Being careful about which folders you open is not enough: dependency installers, build scripts, and post-install hooks from work repositories run as the work user and can read anything it can.

Git has a built-in guard, but only for Git. Running Git in a repository owned by another Windows user fails with a detected dubious ownership error. That stops accidental commits, not other programs reading the files.

  1. From your personal user, remove the inherited Authenticated Users and Users entries so only you, SYSTEM, and Administrators keep access:
icacls "D:\github-personal" /inheritance:r /grant:r "${env:USERNAME}:(OI)(CI)F" "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"
  1. From the work user, do the same for its own folder, so neither side can read the other:
icacls "D:\github-work" /inheritance:r /grant:r "${env:USERNAME}:(OI)(CI)F" "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F"

To undo it, restore inheritance from the drive:

icacls "D:\github-personal" /reset /T

Leaks outside Git

Neither option controls what happens outside your tools. Keep these in mind:

  • Network. Both accounts connect from the same public IP address. GitHub itself can correlate them; other users and organization admins cannot.
  • Commit author name. An identical user.name on both accounts links them to the same person, though not to each other's account. Use a different name if that matters.
  • Copying between sides. Copying a repository, its .git folder, or dotfiles carries remotes, emails, and key paths across. Copy source files only.
  • Past commits. Commits already pushed with the wrong email or signature stay that way unless the history is rewritten.
  • Other applications. Editors and assistants - VS Code, Visual Studio, Copilot - keep their own GitHub sign-ins, separate from everything above.