Why You Should Sign Your Git Commits With GPG
Here is a thing most developers skip: signing git commits with a GPG key. It is one of those “you only notice it when it breaks” problems, so I want to make the case for it and show how to set it up in about ten minutes.
The problem with unsigned commits
Git trusts whatever you put in user.name and user.email. That is it.
There is no verification. Anyone with push access — or who briefly gets it —
can write commits that claim to be from you. An attacker who compromises an
account, a CI bot misconfigured, even a curious contractor with too much
access can all author commits under your name. Git will not blink.
Signing a commit ties it to a GPG private key only you hold. GitHub shows a green Verified badge on every signed commit. Unverified commits still work, but they are a yellow flag in a security audit. And some repos enforce it: with “require signed commits” on in branch protection, unsigned pushes are rejected outright.
The practical reasons are:
- Traceability: you can prove, cryptographically, that a release contains only commits made by known developers.
- Non-repudiation: a signed commit cannot be plausibly disowned.
- Consistent protection rules: one environment with approval requirements is easier to manage than scattered workarounds. Same idea: centralize trust, enforce it in one place.
Setting it up
Install gnupg:
brew install gnupg
Generate a key — RSA 4096 is a safe default, just follow the prompts:
gpg --full-generate-key
Find your key ID:
gpg --list-secret-keys --keyid-format LONG
# sec rsa4096/ABCD1234EFGH5678 2026-06-16 [SC]
The long hex after rsa4096/ is your key ID. Tell git to use it and to sign
every commit automatically:
git config --global user.signingkey ABCD1234EFGH5678
git config --global commit.gpgsign true
Export your public key and add it to GitHub under Settings → SSH and GPG keys → New GPG key:
gpg --armor --export ABCD1234EFGH5678 | pbcopy
Your next git commit will prompt for your passphrase in the terminal and
GitHub will show the Verified badge from then on.
To lock or not to lock
When you generate the key, GPG asks for a passphrase. You have three real options:
No passphrase: the key is protected only by filesystem permissions. Fast, zero friction, and fine if your machine is encrypted and you are the only user. The risk is that anyone who gets access to your disk (stolen laptop, compromised account) can sign commits as you with no additional barrier.
Passphrase, cached forever: you type it once after boot and the agent holds it until you restart. This is what most people end up with. You get the security benefit of the passphrase existing without the daily friction of typing it.
Passphrase, cached for N seconds: the agent forgets the passphrase after a timeout and asks again. This is the right choice if you step away from your machine often or work in shared environments.
Setting the cache timeout
The gpg-agent controls how long a passphrase stays cached. Edit (or create)
~/.gnupg/gpg-agent.conf:
default-cache-ttl 3600
max-cache-ttl 86400
default-cache-ttl is how long after the last use before the agent forgets
the passphrase (3600 means one hour of inactivity). max-cache-ttl is the
hard ceiling regardless of activity (86400 is one day). After that the agent
always asks again, even if you have been committing non-stop.
Apply the change without restarting:
gpgconf --kill gpg-agent
The agent restarts automatically on the next use. If you want it gone immediately until next prompt, you can also clear the cache manually:
gpg-connect-agent reloadagent /bye
My setup is one hour idle, one day hard limit. I commit in bursts and I do not want to type my passphrase every ten minutes, but I also do not want a long unattended session to silently sign things without me knowing. That balance feels right. If you are on a shared or remote machine, tighten it down.
The amend escape hatch
If a commit slips through unsigned — an agent that was not running, a non-interactive shell — you can retroactively sign it and re-push:
git commit --amend --no-edit -S
git push --force-with-lease
Minor friction, worth the tradeoff for knowing every commit with your name actually came from you.
This post was written with the help of AI (Claude by Anthropic).