GPG Signing Git Commits — Complete Guide
In this tutorial, you'll learn about GPG Signing Git Commits. We cover key concepts, practical examples, and best practices.
GPG signing cryptographically verifies that a commit or tag was created by you — not an imposter — by attaching your digital signature to each Git object.
In this tutorial, you'll learn GPG signing for Git commits and tags — how to generate a GPG key, configure Git to sign automatically, verify signatures, and enforce signed commits across your team. Signed commits prevent impersonation, prove authorship, and are required by many open source projects and enterprise security policies. By the end, you'll have a fully configured signing workflow.
Real-world use: DodaTech requires signed commits for all releases across Doda Browser, DodaZIP, and Durga Antivirus Pro. GitHub shows verified badges on signed commits, and CI pipelines verify signatures before deploying.
flowchart LR A[Generate GPG Key] --> B[Configure Git] B --> C[git config user.signingkey] C --> D[Sign Commits] D --> E[git commit -S] D --> F[git tag -s] E --> G[Upload Public Key] G --> H[GitHub / GitLab] G --> I[Keyserver] H --> J[Verified Badge] F --> K[Verified Tag]
Generating a GPG Key
Create a GPG key pair for signing.
# Generate a new GPG key
gpg --full-generate-key
# Follow the prompts:
# 1. Select key type: RSA and RSA (default)
# 2. Key size: 4096
# 3. Expiration: 0 (never expires) or a specific date
# 4. Real name: Jane Doe
# 5. Email: jane@dodatech.com
# 6. Comment: DodaTech Engineering
# 7. Passphrase: (enter a strong passphrase)
Expected output:
gpg: key A1B2C3D4E5F6A7B8 marked as ultimately trusted
gpg: revocation certificate stored as '/home/jane/.gnupg/openpgp-revocs.d/A1B2C3D4E5F6A7B8.rev'
public and secret key created and signed.
pub rsa4096 2026-06-24 [SC]
A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B
uid Jane Doe (DodaTech Engineering) <jane@dodatech.com>
sub rsa4096 2026-06-24 [E]
Listing and Exporting Keys
Find your key ID and share the public key.
# List your GPG keys
gpg --list-secret-keys --keyid-format=long
# Expected:
# sec rsa4096/A1B2C3D4E5F6A7B8 2026-06-24 [SC]
# A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B
# uid [ultimate] Jane Doe (DodaTech Engineering) <jane@dodatech.com>
# Export your public key (for GitHub/GitLab)
gpg --armor --export A1B2C3D4E5F6A7B8
# Copy the entire output (including ---BEGIN PGP PUBLIC KEY BLOCK---)
Configuring Git to Sign
Tell Git which key to use and when to sign.
# Set your signing key globally
git config --global user.signingkey A1B2C3D4E5F6A7B8
# Sign all commits by default
git config --global commit.gpgSign true
# Sign all tags by default
git config --global tag.gpgSign true
# Set the GPG program (if not in default location)
git config --global gpg.program gpg
# For a specific repository only (omit --global)
git config user.signingkey A1B2C3D4E5F6A7B8
git config commit.gpgSign true
Signing Commits
Create signed commits with the -S flag.
# Sign a single commit
git commit -S -m "feat: implement encrypted storage"
# If gpgSign is configured globally, regular commits are also signed
git commit -m "feat: implement encrypted storage"
# Sign the most recent commit (if you forgot -S)
git commit --amend -S --no-edit
# Verify the signature
git log --show-signature -1
Expected output:
$ git log --show-signature -1
commit a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b
gpg: Signature made Mon 24 Jun 2026 10:00:00 AM UTC
gpg: using RSA key A1B2C3D4E5F6A7B8
gpg: Good signature from "Jane Doe (DodaTech Engineering) <jane@dodatech.com>"
Author: Jane Doe <jane@dodatech.com>
Date: Mon Jun 24 10:00:00 2026 +0000
feat: implement encrypted storage
Signing Tags
Signed tags prove that a release came from an authorized maintainer.
# Create an annotated, signed tag
git tag -s v3.2.0 -m "Release v3.2.0"
# Create a signed tag with a GPG key different from the default
git tag -u OTHERKEYID v3.2.0 -m "Release v3.2.0"
# Verify the tag signature
git tag -v v3.2.0
Expected output:
$ git tag -v v3.2.0
object a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b
type commit
tag v3.2.0
tagger Jane Doe <jane@dodatech.com> 1719240000 +0000
gpg: Signature made Mon 24 Jun 2026 10:00:00 AM UTC
gpg: using RSA key A1B2C3D4E5F6A7B8
gpg: Good signature from "Jane Doe (DodaTech Engineering) <jane@dodatech.com>"
Release v3.2.0
Adding Your Key to GitHub
GitHub shows a verified badge next to signed commits.
# 1. Export your public key
gpg --armor --export A1B2C3D4E5F6A7B8 > pubkey.asc
# 2. Copy the content
cat pubkey.asc
# 3. Add to GitHub:
# Settings → SSH and GPG keys → New GPG key
# Paste the entire armored key block
For GitLab: Settings → GPG Keys → Add key.
Verifying Signatures in CI
Enforce signed commits in your CI/CD pipeline.
# .github/workflows/verify-signatures.yml
name: Verify Commit Signatures
on: [pull_request]
jobs:
verify-signatures:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify all commits in PR are signed
run: |
BASE=$(git rev-parse origin/${{ github.base_ref }})
HEAD=$(git rev-parse HEAD)
for commit in $(git rev-list $BASE..$HEAD); do
echo "Verifying: $commit"
if ! git verify-commit $commit 2>/dev/null; then
echo "FAIL: Commit $commit is not signed or signature is invalid"
exit 1
fi
echo "PASS: $commit"
done
Using SSH Signing (Alternative)
Git 2.34+ supports SSH signatures as an alternative to GPG.
# Configure SSH signing
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
# Sign with SSH
git commit -S -m "feat: SSH signed commit"
# Add SSH public key to GitHub
# Settings → SSH and GPG keys → New SSH key
# Provide the same public key
# Verify
git log --show-signature -1
Revoking and Rotating Keys
Handle compromised or expired keys.
# Generate a revocation certificate (created during key generation)
gpg --gen-revoke A1B2C3D4E5F6A7B8 > revoke.asc
# Revoke the key
gpg --import revoke.asc
# Upload the revoked key to keyserver
gpg --keyserver keyserver.ubuntu.com --send-keys A1B2C3D4E5F6A7B8
# Generate a new key and switch
gpg --full-generate-key
git config --global user.signingkey NEWKEYID
# Update GitHub with new key
Common Errors
gpg: signing failed: secret key not available— The key ID inuser.signingkeydoesn't match any secret key on this machine. List keys withgpg --list-secret-keysand use the correct key ID.gpg: can't sign: No pinentry— GPG can't prompt for your passphrase. SetGPG_TTY=$(tty)in your shell profile, or installpinentry-ttyorpinentry-gnome3.gpg: cancelled by user— The passphrase prompt was cancelled or timed out. Rungpg --sign --armorto test passphrase entry separately.- Commit shows "Unverified" on GitHub — The public key on GitHub doesn't match the key that signed the commit. Export the exact key used:
gpg --export-secret-key -a KEYID | gpg --importon a fresh machine, then upload that specific key. error: gpg failed to sign the data— GPG agent configuration issue. Restart the agent:gpgconf --kill gpg-agent && gpgconf --launch gpg-agent.
Practice Questions
Challenge
Set up a complete signing workflow. Generate a new GPG key, configure Git to sign all commits globally, make a series of signed commits, export the public key and add it to a test GitHub repository, verify the commits show as verified, then revoke the key and rotate to a new one. Demonstrate that old commits remain verified but new ones use the new key.
Real-World Task
Implement signed commit enforcement for DodaTech's release pipeline. All release tags across Doda Browser, DodaZIP, and Durga Antivirus Pro must be GPG-signed. Create a CI workflow that verifies: every commit on release branches is signed, the signing key belongs to an authorized maintainer, and release tags have valid signatures. Configure branch protection to reject unsigned commits. Document the key rotation process for the team.
Previous: Signed Commits Guide | Related: Git Tags & Releases | Related: Git Hooks
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro