Multiple GitHub Accounts on One Machine

If you have both a personal and a work GitHub account, you need a way to use each from the same machine without logging in and out. The solution is to give each account its own SSH key and tell Git which key to use based on where your repos live on disk.

Two separate concerns need to be managed:

  • Commit identity (name/email) β€” controlled by Git config

  • Authentication (which account) β€” controlled by SSH keys

The two are independent. Commits carry your name/email as a label. Pushes authenticate via SSH keys, which GitHub uses to identify your account.

Step 1: Create Two SSH Keys

Generate one key per GitHub account
$ ssh-keygen -t ed25519 -C "personal@email.com" -f ~/.ssh/id_ed25519_personal
$ ssh-keygen -t ed25519 -C "work@email.com" -f ~/.ssh/id_ed25519_work
  • ed25519 is the recommended algorithm for GitHub β€” modern, secure, and fast.

  • The -C flag is just a comment/label on the key. It has no functional effect.

  • The -f flag sets the output filename.

Step 2: Configure SSH (~/.ssh/config)

~/.ssh/config
Host github-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  • User git is required β€” GitHub’s SSH server only accepts the user git. Your GitHub account is identified by the key, not the username.

  • The Host aliases (github-personal, github-work) tell SSH exactly which key to use, rather than guessing.

Step 3: Upload Public Keys to GitHub

Copy each public key and add it under GitHub β†’ Settings β†’ SSH and GPG keys β†’ New SSH key.

Print public keys to copy
$ cat ~/.ssh/id_ed25519_personal.pub   # add to personal GitHub account
$ cat ~/.ssh/id_ed25519_work.pub       # add to work GitHub account

Step 4: Configure Git

Keep shared config (aliases, etc.) in ~/.gitconfig. Create separate files for each account that include it, then override identity and SSH URL.

~/.gitconfig β€” shared config only, no identity
[alias]
  co = checkout
  br = branch
  ci = commit
  st = status
~/.gitconfig-work
[include]
  path = ~/.gitconfig

[user]
  name = Work Name
  email = work@email.com

[url "git@github-work:"]
  insteadOf = git@github.com:
~/.gitconfig-personal
[include]
  path = ~/.gitconfig

[user]
  name = Personal Name
  email = personal@email.com

[url "git@github-personal:"]
  insteadOf = git@github.com:
Create the files with correct ownership and permissions
$ touch ~/.gitconfig-work ~/.gitconfig-personal
$ chmod 644 ~/.gitconfig-work ~/.gitconfig-personal

Note

Do not put identity ([user]) or [url] rewrites in ~/.gitconfig itself β€” those belong only in the account-specific files. The base file is included by both, so anything there applies everywhere.

Step 5: Create Personal Folder

$ mkdir ~/personal

Note

Only the personal folder needs to be created. Any path outside of ~/personal will automatically use the work config.

Step 6: Set Up Shell Hook (~/.bashrc)

GIT_CONFIG_GLOBAL overrides the path Git uses as its global config. Override cd to update this variable whenever you change directories.

~/.bashrc
_update_git_config() {
    if [[ "$PWD" == ~/personal* ]]; then
        export GIT_CONFIG_GLOBAL=~/.gitconfig-personal
    else
        export GIT_CONFIG_GLOBAL=~/.gitconfig-work
    fi
}

cd() {
    builtin cd "$@"
    _update_git_config
}

_update_git_config   # run once on shell startup

After editing ~/.bashrc, reload it:

$ source ~/.bashrc

Note

The hook runs on cd, not on repo operations directly. This means identity and URL rewriting are applied based on which directory you are in when you run the Git command β€” including when cloning into a new folder that is not yet a git repo.

How It Works

GIT_CONFIG_GLOBAL tells Git which file to use as its global config. The shell hook keeps it pointed at the right account-specific file based on your current directory. Both account files include ~/.gitconfig for shared settings, then add their own identity and URL rewrite on top.

Why not use includeIf "gitdir:..."? That directive only activates inside an existing git repo. When you run git clone from ~/personal/ β€” which is not itself a repo β€” Git reads the global config before the repo exists, so includeIf never fires and the work identity is used instead. The GIT_CONFIG_GLOBAL approach works at the directory level regardless.

insteadOf rewrites remote URLs transparently. When you clone using git@github.com:, Git silently rewrites the URL to use the correct SSH host alias. This means you never have to type the alias manually.

Git rewrites the URL automatically based on your current folder
$ cd ~/personal && git clone git@github.com:someuser/repo.git
# becomes: git@github-personal:someuser/repo.git

Folder

SSH Host Used

Git Identity

~/personal

github-personal

personal@email

Everything else

github-work

work@email