Safeguard
Software Supply Chain Security

How to set up signed commits with GPG

A step-by-step guide to set up GPG signed commits in Git: generate a key, configure Git, publish it to GitHub, verify signatures, and fix common errors.

Priya Mehta
DevSecOps Engineer
7 min read

Every commit you push carries an implicit claim: "I wrote this." Without cryptographic proof, that claim is trivial to forge — anyone who knows your email address can git commit --author="You <you@company.com>" and produce a commit that looks exactly like yours in git log. For teams shipping software through CI/CD pipelines, package registries, and open-source dependencies, that gap is a real supply chain risk: a single spoofed commit can slip malicious code into a release without anyone noticing until it's too late.

This guide shows you how to set up GPG signed commits so every commit and tag you push is cryptographically verifiable. We'll walk through installing GPG, generating a key, wiring it into Git, publishing your key to GitHub, and confirming signatures actually show up as "Verified." By the end, you'll have a repeatable git commit signing tutorial you can apply across every machine and repo you work in.

Step 1: Set Up GPG Signed Commits by Installing and Generating a Key

Before Git can sign anything, you need a working GPG installation and a key pair.

Install GPG:

  • macOS: brew install gnupg
  • Debian/Ubuntu: sudo apt install gnupg
  • Windows: install Gpg4win or use winget install GnuPG.Gpg4win

Check the install and version:

gpg --version

Generate a new key pair. Use the full, interactive generator so you can pick RSA 4096 and a real expiration date (keys that never expire are harder to rotate safely):

gpg --full-generate-key

Choose:

  • Kind of key: RSA and RSA (default)
  • Key size: 4096
  • Expiration: 1y (renew annually — don't pick "never expires")
  • Real name and email: must match the email associated with your GitHub account (or added as a verified email), otherwise GitHub can't associate the signature with your account

You'll be prompted for a passphrase — set one. An unprotected private signing key is only marginally better than no signing at all.

Step 2: Find Your Key ID and Export the Public Key

List your secret keys to get the key ID:

gpg --list-secret-keys --keyid-format=long

You'll see output like:

sec   rsa4096/3AA5C34371567BD2 2026-07-06 [SC] [expires: 2027-07-06]
      1234567890ABCDEF1234567890ABCDEF12345678
uid                 [ultimate] Jane Dev <jane@company.com>
ssb   rsa4096/4BB6D34371567BD3 2026-07-06 [E]

The string after rsa4096/3AA5C34371567BD2 in this example — is your key ID. Export the public key in ASCII-armored form so it can be pasted into GitHub:

gpg --armor --export 3AA5C34371567BD2

Copy the entire block, including the -----BEGIN PGP PUBLIC KEY BLOCK----- and -----END----- lines.

Step 3: Complete the GPG Key Git Setup

This is the core gpg key git setup step: telling Git which key to use and where to find your gpg binary.

git config --global user.signingkey 3AA5C34371567BD2

On macOS, Git sometimes can't locate GPG on its own. Point it explicitly at the binary:

git config --global gpg.program $(which gpg)

On Windows with Gpg4win, this is typically:

git config --global gpg.program "C:/Program Files (x86)/GnuPG/bin/gpg.exe"

If you work across multiple repos with different identities (personal vs. work email), set user.signingkey and user.email per-repo instead of globally, using git config (without --global) inside each repo.

Step 4: Add Your GPG Key to GitHub

Go to GitHub → Settings → SSH and GPG keys → New GPG key, and paste the exported public key block from Step 2. GitHub will parse the key and match it against the email(s) on your account.

If you're on GitLab or Bitbucket, the equivalent settings pages are Preferences → GPG Keys and Personal settings → GPG keys, respectively — the export and setup steps are identical since it's the same GPG standard.

Step 5: Sign Commits and Make Signing the Default

Sign an individual commit with -S:

git commit -S -m "Add rate limiter to auth middleware"

Signing every commit manually is easy to forget, so make it the default for a repo or globally:

git config --global commit.gpgsign true
git config --global tag.gpgsign true

Now every git commit and git tag -a is signed automatically — no flag required. If you use git commit --amend or interactive rebase to rewrite history, re-signing happens automatically too, as long as commit.gpgsign is set.

Step 6: Verify Commit Signatures on GitHub

Push your signed commits and open the repo on GitHub. Verified commits show a green "Verified" badge next to the commit hash in the commit list and PR view. Click the badge to see the key fingerprint GitHub used to confirm it.

You can also verify commit signatures locally without relying on GitHub's UI, which matters if you're auditing history in a private mirror or CI runner:

git log --show-signature -5

or for a single commit:

git verify-commit <commit-hash>

A clean verification prints gpg: Good signature from "Jane Dev <jane@company.com>". This local check is the same trust chain GitHub uses, so it's a reliable way to confirm end-to-end that your git commit signing tutorial setup actually works before you rely on it in production.

Troubleshooting and Verification Checklist

Commits show "Unverified" on GitHub despite being signed locally. The email in your GPG key's UID doesn't match a verified email on your GitHub account. Run gpg --list-secret-keys --keyid-format=long and confirm the uid line matches an email listed under GitHub → Settings → Emails.

gpg: signing failed: No secret key. Your user.signingkey value doesn't match an installed private key, or GPG can't find your keyring. Re-run gpg --list-secret-keys --keyid-format=long and re-set git config --global user.signingkey.

gpg: signing failed: Inappropriate ioctl for device (common in terminal apps, tmux, VS Code). GPG can't prompt for your passphrase in the current TTY. Add this to your shell profile:

export GPG_TTY=$(tty)

Passphrase prompt every single commit is disruptive. Configure gpg-agent to cache your passphrase for a set period by editing ~/.gnupg/gpg-agent.conf:

default-cache-ttl 28800
max-cache-ttl 86400

Key expired. Extend it rather than regenerating from scratch:

gpg --edit-key 3AA5C34371567BD2
gpg> expire

Follow the prompts, then re-export and re-upload the public key to GitHub if the fingerprint changed.

Double-check the whole pipeline before trusting it. Run through this order: gpg --list-secret-keys shows your key → git config --get user.signingkey matches it → git config --get commit.gpgsign returns true → a fresh commit shows gpg: Good signature locally → the same commit shows "Verified" on GitHub.

How Safeguard Helps

Signed commits solve the "who wrote this" problem for individual contributors, but they don't answer the harder question a security team actually needs answered at scale: are all the commits landing in our default branches actually signed, by keys we trust, from contributors we've vetted? Manually eyeballing "Verified" badges doesn't scale past a handful of repos, and unsigned commits from a misconfigured laptop or a compromised CI token can still slip through if enforcement isn't automated.

Safeguard continuously monitors your software supply chain — including source control policy — to catch exactly these gaps. It flags repositories where commit signing isn't enforced, surfaces commits signed by keys that aren't tied to a known, verified contributor identity, and alerts on sudden changes in signing behavior that can indicate a compromised developer account or leaked signing key. Combined with branch protection rules that require signed commits before merge, Safeguard gives you continuous, org-wide assurance that the provenance guarantees you set up in this guide are actually being enforced everywhere — not just on the one repo you tested them in.

If you're rolling out GPG signing across a team or organization, Safeguard can help you verify adoption, catch stragglers, and tie commit signatures back into your broader supply chain security posture alongside SBOM generation, dependency provenance, and build attestation.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.