Check for existing keys, create a key pair, load it into the agent, add it to GitHub, then clone a private repo over SSH. Verified on Ubuntu 22.04. For two GitHub accounts on one machine, continue with ssh-multiple-github-accounts.

The big picture

YOUR MACHINEGITHUB~/.ssh/id_ed25519.pubpublic key · safe to sharessh-agentholds the key, unlocked~/.ssh/id_ed25519private key · never leavesSettings → SSH keysyour public key, storedLogin checkuses the stored public keyRepositoriesopen to that account① upload once (step 5)② challenge③ signed answerunlocks✓ access: "Hi user!"
1 Check2 Generate3 Permissions4 Agent5 Add to GitHub6 Test7 Clone
  • Two linked files: id_ed25519 → private, never shared · id_ed25519.pub → public, paste anywhere.
  • The private key never travels. GitHub sends a challenge only that key can answer.
  • Why SSH over HTTPS: no token to type or renew, and multiple accounts work cleanly via host aliases.

1. Check for existing keys

ls -al ~/.ssh
  • id_ed25519, id_rsa → private keys (keep secret)
  • id_ed25519.pub, id_rsa.pub → public keys (safe to share)
  • config → per-host SSH settings · known_hosts → servers you’ve connected to
  • authorized_keys → keys allowed to SSH into this machine (unrelated to GitHub)
ls ~/.ssh/*.pub 2>/dev/null || echo "No public keys found"   # public keys only
ssh-keygen -lf ~/.ssh/id_ed25519.pub                          # whose key is this? (see comment field)

"Overwrite (y/n)?" means a key already exists

Answering y destroys it, and every server trusting it stops recognising you. Answer n and pick another -f filename.

2. Generate a key pair (ed25519)

ssh-keygen -t ed25519 -C "xuebin@Cygnus-2026-08-29" -f ~/.ssh/id_ed25519
# enter a passphrase when asked (twice)
  • -t ed25519 → modern, short and strong. Use -t rsa -b 4096 only for very old systems.
  • -C → a label only. Name the machine, so you know which key to revoke later.
  • -f → output path; avoids the overwrite prompt.
  • Passphrase: use one. The agent means you type it once per login. Change it later with ssh-keygen -p -f ~/.ssh/id_ed25519.

3. Fix permissions

SSH refuses keys other users can read, with Bad owner or permissions, which looks like a key problem but isn’t.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/config
chmod 644 ~/.ssh/id_ed25519.pub
stat -c '%A %n' ~/.ssh ~/.ssh/id_ed25519   # expect drwx------ and -rw-------

4. Load the key into the ssh-agent

The agent holds the unlocked key in memory, so the passphrase is typed once.

echo $SSH_AUTH_SOCK          # set? an agent is already running (GNOME desktop)
ssh-add ~/.ssh/id_ed25519    # add the key, passphrase once
ssh-add -l                   # list loaded keys

Don't run eval "$(ssh-agent -s)" on a GNOME desktop

It starts a second agent that only exists in that terminal, so keys vanish when it closes. The classic cause of “it worked a minute ago”. Use it only on servers, WSL or a bare TTY, where SSH_AUTH_SOCK is empty.

Load keys automatically on first use, in ~/.ssh/config:

Host *
    AddKeysToAgent yes

5. Add the public key to GitHub

cat ~/.ssh/id_ed25519.pub    # copy the whole line: ssh-ed25519 AAAA... label

GitHub → avatar → Settings → SSH and GPG keys → New SSH key:

  • Title → the machine name · Key type → Authentication Key (not Signing Key, which silently won’t grant access)
  • With several accounts, check which one you’re logged into first, or you’ll get “Repository not found” later.

6. Test the connection

ssh -T [email protected]
# Hi <user>! You've successfully authenticated, but GitHub does not provide shell access.
  • “does not provide shell access” → normal, not an error. The name after Hi = which account you are.
  • First time only: check the host fingerprint before typing yes. ED25519 should be SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU (GitHub publishes the list). This is your only protection against an impostor server.

7. Clone a private repository

git clone [email protected]:OWNER/REPO.git
git remote -v                                            # confirm the URL
 
git remote set-url origin [email protected]:OWNER/REPO.git  # convert an existing HTTPS clone
  • git remote add → creates a remote (fails if origin exists) · git remote set-url → changes it.
  • Multiple accounts → use your host alias (github-personal:OWNER/REPO.git), see ssh-multiple-github-accounts.

Troubleshooting

SymptomLikely causeCheck / fix
Repository not foundprivate repo + key from an account without access, or a typossh -T [email protected] (who am I?), git remote -v
Permission denied (publickey)no key loaded, or not on the accountssh-add -l · ssh -vT [email protected] 2>&1 | grep -i offering
Bad owner or permissionspermissions too openredo step 3
Asks for the passphrase againkey was in a throwaway agentre-add it, set AddKeysToAgent yes
Authenticated as the wrong userSSH offered another key firsthost aliases + IdentitiesOnly yes, see ssh-multiple-github-accounts

Quick reference

ls -al ~/.ssh                                              # what keys exist
ssh-keygen -t ed25519 -C "label" -f ~/.ssh/id_ed25519      # generate
ssh-keygen -lf ~/.ssh/id_ed25519.pub                       # fingerprint
ssh-add ~/.ssh/id_ed25519 && ssh-add -l                    # load and list
cat ~/.ssh/id_ed25519.pub                                  # the half you upload
ssh -T [email protected]                                      # who am I?
ssh -vT [email protected]                                     # ...verbosely
git remote set-url origin [email protected]:OWNER/REPO.git    # HTTPS -> SSH