GPG/GnuPG and PGP has always sucked. But recently, it turns out it’s basically completely broken and many issues won’t be fixed.
But signing commits is good. So now what?
I approached this problem the wrong way initially. I wanted to replace GnuPG, and found withoutboats’ bpb project, which emulates gpg for signing commits with ed25519 keys. This seemed like a great approach, but hasn’t been updated in 8 years.
Don’t use it! The reason it hasn’t been updated - I think - is that in 2026, Git, GitHub, and GitLab support signing commits with SSH keys:
- Git since 2.34, but because Git is modern, the announcement was on a mailing list.
- GitLab: Sign commits with SSH keys (good enough doco)
- GitHub: Terrible doco. The page Signing commits says “You can sign commits locally using GPG, SSH, or S/MIME.”, however recommends GPG. You have to find the page About commit signature verification, and scroll down a lot to find the SSH key signing instructions, and the same is true for the page Telling Git about your signing key. It’s 2026, can we default to SSH now?
But there are some caveats I want to mention, hence this post. Should you follow it, please read the entire post first before following along.
I assume you already have an SSH ed25519 key pair. (There is no reason to use RSA unless you need to, which you don’t with GitHub or GitLab.) I recommend a new key just for signing, as you’ll see shortly. (See GitLab’s generate an SSH key pair for instructions if a key pair is needed. I’ll be using the key name of signing_ed25519 instead of the default id_ed25519, to make it obvious the key is only for signing.)
The key must also be added to the service:
- In GitHub, a key can either be an authentication or signing key. I think you could add the same key as both an authentication or signing key, but don’t!
- In GitLab, a key can be authentication, signing, or both. Again, I don’t recommend using the same key for both.
Warning: Before proceeding, back up your git config if you have GPG signing enabled! You cannot use both GPG and SSH signing, the user.signingkey setting will be overwritten. Also, for the same reason, you cannot (easily) use multiple keys - it is possible though, see later, but requires quite a work-around. So if you use both GitHub and GitLab, you’d want to use the same signing key. This is why it’s good to have separate keys for authenticating and signing. Then they can be rotated separately.
Next, tell Git to use SSH key signing, assumed to be ~/.ssh/signing_ed25519.pub:
git config --global gpg.format "ssh"
git config --global user.signingkey "~/.ssh/signing_ed25519.pub"
git config --global commit.gpgsign true
git config --global tag.gpgsign true
The last two commands enable signing commits and tags by default, otherwise git commit -S, git merge -S (!), and git tag -s must be specified. Of course the flags are different.
The eagle-eyed reader may have spotted that the signing key is specified as the public key. I believe under the hood, Git will ask OpenSSH for the private/secret key. In any case, it seems to just work, assuming your ssh-agent is set up properly. (You can also put the key directly into the config instead of a path; but it should begin with the key:: prefix, e.g. key::ssh-ed25519 .... Many old tutorials just specify it as ssh-ed25519 ..., which is incorrect.)
However, I found these steps to be not enough. It seems you also must tell Git about “allowed signers” to see and verify signatures. GitLab’s instructions do mention this, but it sounded optional.
echo "$(git config --get user.email) namespaces=\"git\" $(cat ~/.ssh/signing_ed25519.pub)" >> ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile "~/.ssh/allowed_signers"
With this step, git log --show-signature works. In fact, I think I had to do this step to get commit signing to work at all. But I could be wrong; it was late.
(I must admit that I currently don’t have a good workflow for sharing an allowed_signers file in a team.)
By the way, as mentioned, there is a way to use different signing keys. Instead of specifying user.signingkey, it’s possible to use gpg.ssh.defaultKeyCommand. If the signing key is not specified, then this runs the program/command, which then returns the signing key to use. So a small utility could map e.g. the git repo origin to a key.