Sign in with Google

How to Set Up SSH Keys for GitHub

Generate an ed25519 key, load it into the agent, and never paste a token into a push again.

intermediate8 min read
dev-setupgitgithubsshdeveloper-tools

GitHub stopped accepting account passwords for Git operations in 2021, so if you clone over HTTPS you are now pasting a personal access token every time your credential helper forgets it — and tokens expire, which means re-generating one on a schedule forever. SSH keys replace that with a key pair your machine proves it holds. Set it up once and pushing becomes silent. This takes about ten minutes.
iWhat you need
A terminal, Git installed, and a GitHub account you can sign into in a browser. OpenSSH ships with macOS and every mainstream Linux distribution, and with Git for Windows — check with ssh -V.
With HTTPS, every push sends a secret — your token — over the wire to be checked. With SSH, the private key never leaves your machine. GitHub holds only the public half and issues a challenge your key signs, so there is nothing to leak in transit and nothing to re-paste. Practically speaking: no expiry to manage, no credential helper quietly losing its cache, and per-machine revocation. If a laptop is stolen, you delete that one key from your GitHub settings and every other machine keeps working.
Generating a second key when you already have a working one is a common way to create a confusing mess. Look first.
macOS
$ls -la ~/.ssh
You are looking for a pair with matching names — id_ed25519 and id_ed25519.pub, or the older id_rsa and id_rsa.pub. The file without the extension is the private key. The .pub file is the public one, the only half that ever leaves your machine. If the directory does not exist or holds nothing but known_hosts, you have no keys and should generate one. If an id_ed25519 pair is already there, skip ahead to loading it into the agent.
macOS
$ssh-keygen -t ed25519 -C "your_email@example.com"
The -C flag adds a comment — use your email so that a year from now you can tell which key belongs to whom in the GitHub list. Press Return to accept the default location, ~/.ssh/id_ed25519. Ed25519 is the right choice over RSA: the keys are short enough to read in one line, signing is faster, and the algorithm avoids the parameter-choice mistakes that made weak RSA keys possible. RSA is only worth reaching for when a server is too old to speak Ed25519, and then it needs -b 4096 to be comparable.
The passphrase decision
A passphrase encrypts the private key on disk, so a stolen laptop does not hand over your GitHub account. The cost is typing it — which ssh-agent reduces to once per login, and the macOS Keychain to once ever. Use one. An empty passphrase means the file alone is enough for anyone who reads it.
ssh-agent is a background process that holds your decrypted key in memory so you type the passphrase once instead of on every push. Start it, then add the key.
macOS
$eval "$(ssh-agent -s)"
macOS
$ssh-add --apple-use-keychain ~/.ssh/id_ed25519
The --apple-use-keychain flag is macOS-only and stores the passphrase in your login Keychain, so the agent can reload the key after a reboot without asking again. On Linux, drop the flag: ssh-add ~/.ssh/id_ed25519 is the whole command. Then make it durable by writing a config file so you never think about the agent again.
text
Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

~/.ssh/config — create it if it does not exist

!UseKeychain is macOS-only
On Linux, leave UseKeychain yes out — OpenSSH will refuse to parse the file and every SSH connection will fail with a bad-configuration error. AddKeysToAgent yes and IdentityFile are fine everywhere.
GitHub needs the contents of the .pub file. One command per platform, so you do not open the wrong file in an editor:
macOS
$pbcopy < ~/.ssh/id_ed25519.pub
Windows
$clip < ~/.ssh/id_ed25519.pub
Linux
$xclip -selection clipboard < ~/.ssh/id_ed25519.pub
On Wayland desktops use wl-copy < ~/.ssh/id_ed25519.pub instead. If neither tool is installed, cat ~/.ssh/id_ed25519.pub prints it and you can select it by hand. A correct public key is one line beginning ssh-ed25519 AAAAC3Nza… and ending in your email comment.
Never share the file without .pub
id_ed25519 — no extension — is the private key. It begins -----BEGIN OPENSSH PRIVATE KEY-----. If you ever paste that into a website, a chat, or a repository, generate a new pair and delete the old one from GitHub immediately. There is no other remedy.

In your browser

  1. 1Click your avatar in the top-right corner and choose Settings.
  2. 2In the left sidebar, under the Access section, click SSH and GPG keys.
  3. 3Click New SSH key.
  4. 4Title: name the machine, not the key — "work MacBook" or "home desktop". This is what you will read when revoking one later.
  5. 5Key type: leave it on Authentication Key.
  6. 6Paste into the Key field, then click Add SSH key. GitHub may ask for your password to confirm.
macOS
$ssh -T git@github.com
The first time, SSH will not recognize the server and asks you to confirm its host key: The authenticity of host 'github.com' can't be established. ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU. That value is published in GitHub's documentation — compare it, then type yes. Doing this check once is what protects you from a machine impersonating GitHub later.
iThe success message looks like an error
You should see: Hi username! You've successfully authenticated, but GitHub does not provide shell access. That is the win. GitHub has no shell to give you — it only needed to confirm who you are, and the username in that line must be your account.
Repositories you cloned before today still point at an HTTPS URL and will keep asking for a token. Check with git remote -v; if the URL starts with https://, repoint it.
macOS
$git remote set-url origin git@github.com:owner/repo.git
Nothing else changes — same history, same branches, same commits. Run git remote -v again to confirm, then push once to prove it. From now on, use the SSH URL from the green Code button when you clone, not the HTTPS one.
GitHub will not let the same public key sit on two accounts, so a work account and a personal one need separate keys. Generate the second with an explicit filename — ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "you@company.com" — then teach SSH to pick between them with a fake hostname.
text
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

~/.ssh/config with a work alias

Now cloning with git@github-work:company/repo.git uses the work key, and the normal git@github.com: form uses your personal one. IdentitiesOnly yes matters more than it looks: without it, SSH offers every key it has in turn, GitHub accepts the first valid one, and you end up authenticated as the wrong account with no obvious explanation.
Git 2.34 and later can sign commits with an SSH key rather than a separate GPG setup, which earns the Verified badge on GitHub with no extra tooling. Three settings:
bash
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

Sign every commit with your SSH key

Then add the same public key to GitHub a second time, exactly as before, but set Key type to Signing Key. GitHub tracks authentication keys and signing keys separately, so one entry does not cover both — and a commit signed with a key GitHub has not been told about shows as Unverified rather than as an error.

Symptom, cause, fix

  1. 1"Permission denied (publickey)." The agent is not offering a key GitHub knows. Run ssh-add -l to list loaded keys; if it says the agent has no identities, add the key again.
  2. 2"Could not open a connection to your authentication agent." The agent is not running in this shell. Run eval "$(ssh-agent -s)" and retry.
  3. 3The passphrase prompt returns after every reboot. Your ~/.ssh/config is missing, or the key was added without --apple-use-keychain on macOS.
  4. 4"Bad configuration option: usekeychain" on Linux. Remove the UseKeychain line — it is a macOS extension.
  5. 5"WARNING: UNPROTECTED PRIVATE KEY FILE!" The permissions are too open. Run chmod 600 ~/.ssh/id_ed25519 and chmod 700 ~/.ssh.
  6. 6Pushes still ask for a username and password. That repo's remote is still HTTPS. Fix it with git remote set-url.
  7. 7You authenticate as the wrong account. Two keys are loaded and IdentitiesOnly yes is missing from your config.
With authentication out of the way, the payoff is in using Git well — How to Use Git covers the model behind the commands and the undo operations worth knowing before you need them. If the ~/.ssh/config editing above felt shaky, How to Use the Command Line covers file permissions, paths, and editing dotfiles properly, and How to Set Up VS Code gets you an editor that handles them without fuss. The Becoming a Software Engineer roadmap puts this kind of tooling in the order it actually gets used.