Rotate GPG signing keys¶
Every .deb package served by Repod is signed with a GPG key. Client machines trust packages from your repository because they have a copy of that key in /etc/apt/trusted.gpg.d/. Rotating the key requires distributing the new public key to clients before the old one stops being trusted. This guide walks through that process safely.
1. When to rotate¶
Rotate your GPG signing key when any of the following apply:
- Key compromise — the private key material has been or may have been exposed. Rotate immediately.
- Annual rotation policy — many security policies require key rotation on a fixed schedule regardless of compromise.
- Expiry approaching — if the key was generated with an expiry date, rotate before the expiry to avoid breaking
apt updateacross your fleet. - Personnel change — a team member with access to the GPG keyring has left the organisation.
Key compromise requires immediate action
If you suspect the private key has been compromised, do not wait — generate a new key immediately and begin distributing it. Accept that there will be a brief window where some machines may see apt update warnings; this is preferable to trusting a compromised signing key.
2. Rotation strategy — dual-key transition¶
Rotating a GPG key in a fleet of machines is safe only when clients trust both the old and new key simultaneously during a grace period. The sequence is:
- Generate new key in Repod — repository begins signing with the new key.
- Distribute the new public key to all client machines (both keys now trusted).
- Wait at least two weeks — all clients have had time to pull the new key.
- Remove the old key from client machines.
At no point should a client be left trusting only the old key while the repository signs with the new one.
3. Step 1 — Generate the new key¶
- Open the Repod web interface and navigate to Settings → GPG.
- Click Generate new key.
- Enter a key name and, optionally, an email address and expiry date.
- Click Generate. Key generation takes a few seconds.
The new key fingerprint appears immediately. The repository will begin re-signing metadata with the new key automatically.
If you have shell access to the host running Repod, you can generate a key directly using the repository's keyring directory:
gpg --homedir /repos/gnupg \
--batch \
--gen-key <<EOF
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: Repod Repository Key 2026
Name-Email: apt@example.com
Expire-Date: 2y
%no-passphrase
%commit
EOF
After generating the key directly, trigger a repository re-sign via the API so Repod picks up the new key:
Note
Repod stores all GPG state in the /repos/gnupg Docker volume. Do not manipulate the default system keyring (~/.gnupg) — changes there have no effect on the repository.
4. Step 2 — Export the new public key¶
After generation, Settings → GPG displays the full fingerprint and an ASCII-armored public key block. Click Copy public key or Download public key to save it.
Verify the exported key is valid:
5. Step 3 — Distribute the new public key to client machines¶
Clients need to trust the new key before the old one is removed. Add it alongside the existing key — do not replace it yet.
- name: Distribute new Repod GPG key
ansible.builtin.get_url:
url: http://repod.example.com/gpg-new.key
dest: /tmp/repod-new.gpg.asc
- name: Install new key alongside existing key
ansible.builtin.command:
cmd: >
gpg --dearmor
-o /etc/apt/trusted.gpg.d/repod-new.gpg
/tmp/repod-new.gpg.asc
creates: /etc/apt/trusted.gpg.d/repod-new.gpg
Tip
Repod also serves the current signing key at http://repod.example.com/gpg.key. After key rotation, this endpoint serves the new key. You can expose the new key at a distinct URL (e.g. /gpg-new.key) via a custom Nginx location block if you need to distribute it independently during the transition.
6. Step 4 — Re-sign the repository¶
If you generated the key via Settings → GPG → Generate key or the API endpoint, re-signing happens automatically. Repod immediately rebuilds the Release, InRelease, and Packages files using the new key.
To trigger a manual re-sign at any time:
curl -s -X POST http://repod.example.com:8000/settings/gpg/resign \
-H "Authorization: Bearer repod_xxxxxxxxxxxxxxxx"
Confirm the new fingerprint appears in the repository metadata:
7. Step 5 — Grace period¶
Leave both keys trusted on all client machines for a minimum of two weeks. During this period:
apt updatesucceeds regardless of which key signs the current metadata.- Any client that missed the initial rollout will pick up the new key on its next configuration management run.
- You have time to verify the rollout is complete before removing the old key.
Notify the relevant teams of the rotation and the planned date for removing the old key.
Warning
Do not skip the grace period, even in test environments used by automated pipelines. A CI runner that only runs occasionally may miss a key update and start failing apt install calls unexpectedly.
8. Step 6 — Remove the old key¶
After the grace period has elapsed and you have confirmed that apt update runs cleanly on all client machines using only the new key:
You may also rename repod-new.gpg to repod.gpg at this point for consistency, ready for the next rotation.
9. Rollback considerations¶
Re-generating the exact same private key from scratch is not possible — private keys cannot be reconstructed. If you need to roll back after generating a new key:
- If the old key backup is available, restore it from your backup archive (see Back up and restore Repod — the GPG keyring is included in
backup.sh). - If the old key backup is not available, you cannot roll back. You must proceed with the new key and complete the client-side rollout.
Warning
This is why the GPG keyring backup (/repos/gnupg/) is classified as High importance in the backup guide. Always verify that your backup includes the gnupg/ directory before rotating keys.
10. Verification commands¶
After the rotation is complete and the old key has been removed, verify that everything is clean:
# On a client machine — should show no warnings
sudo apt update
# Verify the signature on the repository InRelease file
curl -s http://repod.example.com/dists/jammy/InRelease \
| gpg --verify --keyring /etc/apt/trusted.gpg.d/repod-new.gpg -
# Verify the signature on a specific .deb file
gpg --keyring /etc/apt/trusted.gpg.d/repod-new.gpg \
--verify your-package_1.0_amd64.deb.sig \
your-package_1.0_amd64.deb
# Confirm the old key fingerprint no longer appears in trusted keyrings
gpg --no-default-keyring \
--keyring /etc/apt/trusted.gpg.d/repod-new.gpg \
--list-keys
A clean apt update with no NO_PUBKEY warnings is the definitive confirmation that the rotation is complete.