Table of Contents

The Problem

If you sign your Git commits using GPG, you might eventually encounter an error like this when attempting to commit changes:

$ git commit -m "My latest commit"
error: gpg failed to sign the data:
[GNUPG:] KEYEXPIRED 1794083815
[GNUPG:] KEY_CONSIDERED 111122223333444455556666AAAABBBBCCCCDDDD 3
gpg: skipped "111122223333444455556666AAAABBBBCCCCDDDD": Unusable secret key
[GNUPG:] INV_SGNR 9 111122223333444455556666AAAABBBBCCCCDDDD
[GNUPG:] FAILURE sign 54
gpg: signing failed: Unusable secret key
fatal: failed to write commit object

Best security practices recommend setting an expiration date (typically 1 or 2 years) when generating GPG keys. When that expiration date passes, GPG will refuse to use the secret key to sign data, returning Unusable secret key.

Fortunately, an expired key does not mean you need to create a new keypair. You can extend the validity of your existing primary key and subkeys without losing your signature history or breaking previous commit verifications.


Step 1: Identify Your Expired Key

First, find your key ID. You can check the key configured for Git:

git config user.signingkey

Or list all secret keys in your GPG keyring with long key IDs:

gpg --list-secret-keys --keyid-format LONG

You will see output similar to:

sec   rsa4096/3AA5C34371567BD2 2024-11-08 [SC] [expired: 2026-11-07]
      111122223333444455556666AAAABBBBCCCCDDDD
uid                   [ expired] User <user@example.com>
ssb   rsa4096/42B317FD4BA89230 2024-11-08 [E] [expired: 2026-11-07]

Note your key ID (the 16-character string 3AA5C34371567BD2 or the full fingerprint 111122223333444455556666AAAABBBBCCCCDDDD). Notice that both the primary key (sec) and the subkeys (ssb) may be marked as [expired].


Step 2: Extend the Expiration Date

Open the interactive GPG key editor:

gpg --edit-key <YOUR_KEY_ID>

1. Update the Primary Key

At the gpg> prompt, run expire:

gpg> expire

GPG will ask for the validity period:

Changing expiration time for the primary key.
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 

Type your preferred extension (e.g., 1y for one year or 2y for two years) and hit Enter. Confirm the date when prompted:

Is this correct? (y/N) y

If prompted, enter your GPG passphrase to authorize the modification.

2. Update the Subkeys

Most GPG keys use separate subkeys for signing ([S]), encryption ([E]), or authentication ([A]). If your signing subkey is expired, you also need to update its expiration date.

In the key listing within gpg>, identify the index of each subkey (e.g., 1, 2).

Select the first subkey:

gpg> key 1

(An asterisk * will appear next to ssb indicating it is selected.)

Now run expire again:

gpg> expire

Choose the same expiration period (e.g., 1y or 2y) and confirm with y.

Deselect the subkey by entering key 1 once more:

gpg> key 1

If you have additional subkeys (e.g., key 2), repeat the process for each one.

3. Save the Changes

Save and quit the GPG prompt:

gpg> save

Your local key is now valid again!


Step 3: Update Remote Git Hosts (GitLab, GitHub)

Even though your local GPG tool can now sign commits, remote platforms like GitLab and GitHub will continue to mark new commits as Unverified until they have your updated public key containing the new expiration date.

1. Export the Updated Public Key

Export your public key in ASCII-armored format:

gpg --armor --export <YOUR_KEY_ID>

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

2. Update GitLab

  1. Navigate to User Settings -> GPG Keys (https://gitlab.com/-/user_settings/gpg_keys).
  2. Delete the expired key entry.
  3. Paste the newly exported key block into the Key field and click Add key.

3. Update GitHub (if applicable)

  1. Navigate to Settings -> SSH and GPG keys (https://github.com/settings/keys).
  2. If GitHub does not automatically refresh the expiration, delete the expired key and paste the newly exported public key.

4. (Optional) Sync with Public Keyservers

If you originally published your key to a public keyserver:

gpg --send-keys <YOUR_KEY_ID>

Step 4: Exporting the GPG Key to Other Devices

If you develop across multiple machines (such as a laptop, desktop, or remote server) and want to use the same GPG key for signing commits, you can transfer the keypair to your other devices.

1. Export from the Source Device

Export both the public key and the private (secret) key to ASCII-armored files:

# Export public key
gpg --armor --export <YOUR_KEY_ID> > public.key

# Export secret key (prompts for passphrase)
gpg --armor --export-secret-keys <YOUR_KEY_ID> > private.key

Security Note: private.key contains your private key material. Transfer it securely (e.g., using scp over SSH or an encrypted USB drive). Never transmit it over unencrypted channels or commit it to a repository.

2. Import on the Target Device

On the target machine, import both files into the local keyring:

gpg --import public.key
gpg --import private.key

Once imported, delete the exported private.key file to prevent leaving sensitive keys on disk:

shred -u private.key

(On macOS or systems without shred, use rm private.key.)

3. Set Key Trust

Imported keys initially have an “unknown” trust level on a new keyring. To prevent warnings when signing commits, mark the key as ultimately trusted:

gpg --edit-key <YOUR_KEY_ID>

At the gpg> prompt:

gpg> trust

Select 5 (I trust ultimately), confirm with y, and save:

gpg> save

4. Configure Git on the Target Device

Configure Git to use the key for commit signing:

# Set your signing key
git config --global user.signingkey <YOUR_KEY_ID>

# Enable commit signing by default
git config --global commit.gpgsign true

If you encounter issues with GPG prompting for your passphrase in the terminal, ensure GPG_TTY is set in your shell configuration (~/.bashrc or ~/.zshrc):

export GPG_TTY=$(tty)

Quick Tip: Bypass Signing in an Emergency

If you are in a rush and need to commit immediately before you have a chance to update your key, you can instruct Git to bypass signing for a single commit:

git commit --no-gpg-sign -m "Urgent commit without signature"