Every push to a self-hosted Git server starts with the same question: who are you? SSH keys answer it without passwords - but only if they are set up right, and “set up right” takes about five minutes once.
One modern key#
ssh-keygen -t ed25519 -C "stefan@work-laptop"
ed25519 is the current default: short keys, fast verification, no size decisions. The comment (-C) is a label, not a login - use the machine or purpose, so that when you revoke a lost laptop’s key next year you can tell which one it was. A passphrase on the key is worth it; the SSH agent means you type it once per session, not once per push.
Hand the public half to the server#
The file ending in .pub is the one you share. On a self-hosted server it goes into the user’s account in the web UI (or authorized_keys on a bare setup). The private half never leaves your machine - a server asking for it is a server you should stop using.
Then test:
ssh -T [email protected]
A greeting means you are done; “Permission denied (publickey)” means read on.
When it does not work#
Three causes cover almost every failure:
- The agent is not offering the key:
ssh-add -llists what the agent holds;ssh-add ~/.ssh/id_ed25519adds it. - SSH is offering the wrong key first. Some servers drop the connection after a few failed offers. Pin the right one in
~/.ssh/config:
Host yourgit.example.com
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
- The remote URL is HTTPS while your account expects SSH (or the reverse). Check with
git remote -v- the fix is changing the URL, and if a fork or second server is involved, the two-remote layout from Keeping a Fork in Sync keeps them straight.
Per-machine, not per-person#
Treat keys as belonging to machines, not to you: laptop key, desktop key, CI deploy key. Revoking a device is then a one-line delete on the server, and the audit log of who pushed what stays meaningful - which matters more once branch protection makes pushes consequential.
Set it up once per machine, and the question “who are you?” stops appearing in your day entirely.
Share this post
Keep reading
Annotating Commits with git notes: Metadata Without Rewriting History
Some things belong on a commit but not in it: review links, benchmark results, the ticket nobody remembered to reference. git notes adds them after the fact, no rebase required.
Backing Up Your Git Server So It Actually Restores
Every clone is a partial backup - but hooks, config and server state live outside the repo. Mirror, bundle, copy off-site, and rehearse the restore.
Cherry-Picking: One Commit Without the Branch
Cherry-pick copies a single commit onto your branch and leaves the rest behind. Right for hotfixes across release branches, wrong as a sync strategy.