Skip to content

GPG Signing Git Commits — Complete Guide

DodaTech Updated 2026-06-24 7 min read

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

  1. gpg: signing failed: secret key not available — The key ID in user.signingkey doesn't match any secret key on this machine. List keys with gpg --list-secret-keys and use the correct key ID.
  2. gpg: can't sign: No pinentry — GPG can't prompt for your passphrase. Set GPG_TTY=$(tty) in your shell profile, or install pinentry-tty or pinentry-gnome3.
  3. gpg: cancelled by user — The passphrase prompt was cancelled or timed out. Run gpg --sign --armor to test passphrase entry separately.
  4. 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 --import on a fresh machine, then upload that specific key.
  5. error: gpg failed to sign the data — GPG agent configuration issue. Restart the agent: gpgconf --kill gpg-agent && gpgconf --launch gpg-agent.

Practice Questions

Why should I sign Git commits?

Signing proves that a commit was created by you, not an imposter with your name and email. It prevents impersonation in open source projects, satisfies compliance requirements (SOC2, FedRAMP), and creates a verifiable chain of custody for releases.

What is the difference between signing commits and signing tags?

Commit signing proves the authenticity of individual changes. Tag signing proves the authenticity of a release, covering the entire set of changes since the last tag. Both use the same GPG/SSH key. Tags are typically signed for releases; commits can be signed individually or automatically.

How do I sign commits on a CI/CD machine?

Import the signing key into the CI environment via encrypted secrets: echo "$GPG_PRIVATE_KEY" | gpg --import. Use a dedicated CI key with restricted permissions, not your personal key. Never store private keys in plaintext.

What happens when a GPG key expires?

Commits signed with an expired key still show as valid (they were valid when signed) but new commits can't be signed. Generate a new key, update user.signingkey, add the new public key to GitHub/GitLab, and optionally re-sign old tags.

Can I use the same key for SSH authentication and Git signing?

Yes, with Git 2.34+ and SSH signing. Configure gpg.format ssh and point user.signingkey to your SSH public key. Add the same public key to GitHub's SSH and GPG keys sections. This reduces key management overhead

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