When you set up a fresh machine — a dev box or a server — and try to clone, pull or push your GitHub repos, you often get stuck on authentication. First, get clear on when authentication is actually needed:
- Read-only clone of a public repo → no auth needed, just pull;
- Accessing a private repo (clone / pull / push) → auth required;
- Pushing to any repo (even a public one) → auth required too.
In other words, apart from pulling public repos, any privileged operation on a repo needs an “identity” first. Set it up once, and the machine can clone / pull / push normally afterward.
The main idea: there are two routes to an identity — SSH keys or a token (PAT) — and the difference is “how many doors this key opens, and what it can do”.
How public/private key authentication works
Both SSH options below rest on a key pair, so let’s get the verification flow straight first:
- Private key: stays on your machine (in
~/.ssh/) and is never sent anywhere; - Public key: can be shared, and you register it with GitHub.
They are asymmetric — a signature made with the private key can only be verified by the matching public key, and you can’t derive the private key from the public one. The flow goes roughly like this:
- You register the public key with GitHub ahead of time (account SSH keys, or a repo’s Deploy keys);
- On connecting, your machine starts an SSH session claiming “I hold the private key matching a certain public key”;
- GitHub uses the registered public key to issue a random challenge;
- Your machine signs the challenge with the private key and sends the result back;
- GitHub verifies that signature with the public key — if it checks out, you’ve proven you hold the matching private key, your identity is established, and you’re let in.
your machine GitHub
│ ① I hold the private key for ... │
│ ───────────────────────────────▶ │
│ ② random challenge │
│ ◀─────────────────────────────── │
│ ③ sign the challenge with the │
│ PRIVATE key, send it back │
│ ───────────────────────────────▶ │
│ ④ verify with registered PUBLIC │
│ key → access granted │
The key point: the private key never leaves your machine — all that travels over the network is “proof computed with the private key”. So even if the whole exchange is intercepted, the private key can’t be stolen. That’s why the public key can be shared freely while the private key must stay secret.
Option 1: SSH key (account-level, most general)
A key tied to your GitHub account works for all your repos and all of clone / pull / push — ideal for a dev machine you use regularly.
① Generate a key (just press Enter through the prompts):
ssh-keygen -t ed25519 -C "my-laptop"
It asks for a save path (Enter for the default ~/.ssh/id_ed25519) and a passphrase (Enter for none). It produces two files: id_ed25519 (the private key, keep it secret) and id_ed25519.pub (the public key).
-t ed25519: key type — shorter and more secure than the old RSA, the current recommendation;-C "my-laptop": a comment/label, purely to help you recognize it later; no effect on function.
② Add the public key to your account:
cat ~/.ssh/id_ed25519.pub # copy this
GitHub → Settings → SSH and GPG keys → New SSH key → paste.
③ Use SSH URLs (a default-named key is picked up by SSH automatically, no extra config):
git clone [email protected]:ldddi/your-repo.git
ssh -T [email protected] # verify: seeing "Hi ldddi!" means it works
- Pro: set up once, covers all your repos and all operations;
- Con: broad access — if the machine is compromised, all your repos are exposed. So on servers, the read-only Deploy Key below is a better fit.
Option 2: Deploy Key (bound to a single repo — best for servers/CI)
An SSH key scoped to one repo, and one you can make read-only. Ideal for a VPS pulling one project to deploy — minimal access, so even if the machine is breached, only that one repo’s read is affected.
① Generate it on that machine (again, just press Enter):
ssh-keygen -t ed25519 -C "deploy-my-blog"
② Add the public key to the “repo” (not the account): GitHub → open the repo → Settings → Deploy keys → Add deploy key → paste ~/.ssh/id_ed25519.pub. For pull-only, don’t tick “Allow write access” (read-only is safest).
③ Clone / switch the remote to the SSH URL:
git clone [email protected]:ldddi/my-blog.git
# already cloned over https? switch the remote:
git remote set-url origin [email protected]:ldddi/my-blog.git
Advanced: one machine pulling multiple private repos
A Deploy Key binds to a single repo, and the default name id_ed25519 only exists once per machine. So for multiple repos, generate a separate key per repo with -f to give each a different filename:
ssh-keygen -t ed25519 -C "deploy-blog" -f ~/.ssh/key-blog -N ""
ssh-keygen -t ed25519 -C "deploy-repoA" -f ~/.ssh/key-repoA -N ""
(Here -f sets the filename and -N "" sets an empty passphrase — exactly so you can keep multiple keys apart and avoid interactive prompts.)
But SSH won’t use custom-named keys on its own (it only looks for default names like id_ed25519). So on this machine (not GitHub), edit ~/.ssh/config to tell it “which key for which repo”:
# This file lives on the machine that pulls: ~/.ssh/config
Host github-blog # a custom alias, name it anything
HostName github.com
User git
IdentityFile ~/.ssh/key-blog # this alias uses this private key
IdentitiesOnly yes
Host github-repoA
HostName github.com
User git
IdentityFile ~/.ssh/key-repoA
IdentitiesOnly yes
Host github-blog: an alias — since both repos are on github.com, the alias is how you pick which key to use;HostName/User git: it still actually connects to github.com with the fixed usernamegit;IdentityFile: which private key this alias uses;IdentitiesOnly yes: use only this key, don’t try the machine’s other keys (avoids wrong-key failures).
When cloning, replace github.com in the URL with your alias:
git clone git@github-blog:ldddi/my-blog.git # note: github-blog, not github.com
For a single repo, just use the default name
id_ed25519— SSH uses it automatically and you don’t need this config at all.
Option 3: Personal Access Token (PAT, over HTTPS)
If you’d rather not use SSH, or you’re in CI, use a token over HTTPS:
- GitHub → Settings → Developer settings → Fine-grained token, scope it to specific repos with the permissions you need (
Contents: Readfor pull only,Read and writefor push); - Clone:
git clone https://<TOKEN>@github.com/ldddi/your-repo.git
- Pro: permissions can be as fine as “read-only on one repo”; good for CI and pure-HTTPS setups;
- Con: tokens expire, so they need safe storage and periodic rotation.
How to choose
| Method | Can access | Can do | Best for |
|---|---|---|---|
| Account SSH key | all your repos | clone / pull / push | your own dev machine |
| Deploy Key | a single repo | read-only (write optional) | server / CI pulling one repo |
| PAT (HTTPS) | scopable per repo | per granted permission | CI, HTTPS environments |
Summary
- Read-only cloning of public repos needs no auth; private repos, and any push, do;
- Your own dev machine → account SSH key, set up once, covers every repo and operation;
- A server pulling a single repo to deploy → read-only Deploy Key, minimal access and safest;
- CI / HTTPS → PAT.