Sign Git commits and tags with GnuPG or SSH keys
A signed Git commit or tag proves that someone holding your key created it. Git can sign with an OpenPGP key through GnuPG (the default) or, since Git 2.34, with an SSH key. Set user.signingkey, turn on commit.gpgsign, and upload the same public key to GitHub or GitLab to get the “Verified” badge.
The examples were tested in November 2026 with Git 2.47, GnuPG 2.4.7 and OpenSSH 10.0 on Debian 13. The newest Git release as of November 2026 is 2.56.0, and the settings below are the same there.
What signing does and does not prove
Git records an author name and email in each commit, but anyone can type any name. A signature adds proof that the commit object, including its tree and parent commits, was signed by a specific key. It does not prove the code is good, and it says nothing about commits that are not signed. Signing is most useful for release tags and for projects that check signatures before merging.
Sign with your OpenPGP key
You need a key pair with signing capability (create one). Find its fingerprint:
gpg --list-secret-keys --keyid-format=long
Tell Git which key to use, and sign everything by default:
git config --global user.signingkey 47C8795201EE2CC8465E9A646DB662AD4C80E1C2
git config --global commit.gpgsign true
git config --global tag.gpgSign true
Git passes user.signingkey unchanged to gpg’s --local-user, so a fingerprint, long key ID or email all work; the full fingerprint is the most precise. The email in your Git configuration (user.email) should be one of the user IDs on the key, or the hosting sites will not show the commit as verified.
Without the defaults, sign one commit with -S and one tag with -s:
git commit -S -m "Fix the parser"
git tag -s v1.2.0 -m "Release 1.2.0"
If gpg cannot ask for your passphrase in a terminal session, set GPG_TTY in your shell start-up file. Both GitHub’s documentation and the gpg-agent manual give the same line:
export GPG_TTY=$(tty)
With commit.gpgsign turned on, a long rebase signs every rewritten commit again; the Git manual suggests using an agent so you do not type the passphrase each time (gpg-agent does this for you). Keys on a YubiKey or other OpenPGP card work the same way: Git calls gpg, gpg asks the card.
Check a signature
git log --show-signature -1
git verify-commit HEAD
git verify-tag v1.2.0
A good signature looks like this:
gpg: Signature made Wed 04 Nov 2026 02:02:43 AM EST
gpg: using EDDSA key 47C8795201EE2CC8465E9A646DB662AD4C80E1C2
gpg: Good signature from "Test User <test@example.org>" [ultimate]
git verify-commit exits with status 0 for a good signature, so you can use it in scripts. For a quick overview of many commits use the %G? format:
git log --format='%h %G? %GK %GS' -10
| Code | Meaning |
|---|---|
| G | Good (valid) signature |
| U | Good signature, unknown validity (you have not certified or trusted the key) |
| X | Good signature that has expired |
| Y | Good signature made by an expired key |
| R | Good signature made by a revoked key |
| B | Bad signature |
| E | Cannot be checked, for example the key is missing |
| N | No signature |
To check other people’s commits you need their public keys in your keyring: fetch them by fingerprint from a keyserver or by email via WKD. git merge --verify-signatures aborts a merge unless the tip commit of the merged branch is signed with a valid key; by default that requires a key with at least marginal trust (adjust with gpg.minTrustLevel).
Sign with an SSH key
Git 2.34 added SSH signing. Its release notes warn that it does not work with ssh-keygen from OpenSSH 8.7, so use OpenSSH 8.8 or later. If you already have an SSH key, this is the quickest setup:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgSign true
user.signingkey can point to the private key or, when the key is loaded in ssh-agent, to the public key. Git calls ssh-keygen to sign.
SSH has no web of trust, so Git needs a list of keys you accept before it can verify anything. Without it, git log --show-signature prints “gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification”. Create the file:
echo "$(git config user.email) namespaces=\"git\" $(cat ~/.ssh/id_ed25519.pub)" >> ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
Each line holds the principals (email addresses), options and the public key, as described under “ALLOWED SIGNERS” in ssh-keygen(1). The namespaces="git" option limits that key to Git signatures. Add a line for each colleague whose commits you check. A verified commit then shows:
Good "git" signature for test@example.org with ED25519 key SHA256:+IBb4Ux59J1LGihbXkzBJPBE8mY5iLH+KB9ZreK5vmc
Git treats a key listed in the allowed signers file as fully trusted; a key that is not listed has undefined trust, and git verify-commit fails.
OpenPGP or SSH?
- SSH is simpler if you already use SSH keys and only need hosting sites to show “Verified”. There is no expiry, revocation publishing or keyserver lookup.
- OpenPGP keys can expire, be revoked, be found by fingerprint or email, and be checked by anyone without a shared signers file. That makes them a good fit for signed release tags that outsiders verify.
GitHub: the Verified badge
- Export your public key:
gpg --armor --export FPR(or copy your.pubSSH key). - On GitHub, open Settings → SSH and GPG keys (in the “Access” section of the sidebar) and click New GPG key, or for SSH click New SSH key and add it as a signing key. A key you already uploaded for SSH login must be uploaded again as a signing key.
- Make sure the email in the key is a verified email on your GitHub account and matches your commit email. GitHub says the key “must be associated with a verified email that matches your committer identity”. To keep your address private, you can use your GitHub-provided no-reply address in both places.
GitHub lists RSA, ElGamal, DSA, ECDH, ECDSA and EdDSA as supported GPG key algorithms. It shows Verified when the signature checks out and Unverified when a commit is signed but cannot be verified; unsigned commits show no status by default. With the optional vigilant mode turned on, your unsigned commits are also marked Unverified, and a commit whose author differs from the committer can be shown as Partially verified.
GitHub records verification when the commit is pushed. Its documentation says previously verified commits keep their status even if signing keys are later rotated or revoked.
GitLab: the Verified badge
- In GitLab, select your avatar, then Edit profile → Access → GPG keys, then Add new key, and paste the exported public key. For SSH signing, add the SSH key with usage type “Authentication & Signing” or “Signing”.
- GitLab requires that one email in the GPG key matches a verified email of your GitLab account, and that the committer email matches it too. It ignores emails from revoked user IDs. For SSH signing, the committer email must match a verified email on your account.
GitLab supports GPG keys, SSH keys and X.509 certificates. Its SSH documentation draws a useful line: revoking a key in GitLab marks earlier commits signed with it as Unverified, while deleting the key leaves earlier commits as they were. New commits stay Unverified until you add a key again.
Common problems
- “gpg failed to sign the data” and “No secret key”
user.signingkeynames a key that is not in your secret keyring (checkgpg -K), or the key has expired. Extend it withgpg --quick-set-expire(how) and upload the updated public key to the hosting site.- No passphrase prompt in a terminal
- Set
export GPG_TTY=$(tty)and open a new shell. - Signed locally, Unverified on the site
- Usually the commit email is not on the key, is not verified on the account, or the key on the site is an older copy without your newer subkey. Upload the current public key again.
- Git uses the wrong gpg program
- Set it explicitly:
git config --global gpg.program gpg(or the full path, for example on Windows with Gpg4win).
Sources
- git-config documentation (gpg.*, user.signingKey, commit.gpgSign, tag.gpgSign), Git
- git-log documentation (%G? placeholders), Git
- Git 2.34 release notes, Git project
- ssh-keygen(1), Debian 13 manual pages
- About commit signature verification, GitHub Docs
- Telling Git about your signing key, GitHub Docs
- Adding a GPG key to your GitHub account, GitHub Docs
- Associating an email with your GPG key, GitHub Docs
- Signed commits, GitLab Docs
- Sign commits with GPG, GitLab Docs
- Sign commits with SSH keys, GitLab Docs