GPG Migration Post Mortem
Setting the stage
I use a YubiKey for storing my GPG identity so I don’t carelessly toss it away on accident.
A requirement for this kind of setup is that the GPG key(s) live on the YubiKey itself so that it can only be used when the YubiKey is plugged into a device.
Back when I was initially setting this up I followed this excellent guide made by Dr. Duh over on GitHub. It goes into immense detail about how to provision the GPG keys needed, as well as actually getting them onto the YubiKey.
I followed the guide to the letter, but due to a major skill-issue on my part, I ended up accidentally deleting my certify and subkeys backups meaning that in 2 years time from creation the subkeys would expire and there was nothing I could do about it.
Present day
Having grown wiser since the little incident of essentially erasing my future identity, I decided I couldn’t put it off any longer and that it was time to generate some new keys.
First things first, I had to figure out how much this change would affect. Under normal operation, assuming a key hasn’t been compromised extending the expiry date of a subkey is a totally fine thing to do and it significantly reduces the overhead as no services require updating/informing about the new key(s).
Determining blast radius
I use my GPG keys for the following:
- Signing Git commits
- Deriving my primary SSH identity
- Authentication
- Encrypting/decrypting secrets
That’s quite a bit. And with not infinite time to spare, I got to work on migrating to a new GPG key.
The migration
Keygen is the easy part. The part I messed up last time is what comes after: the key has to exist somewhere that isn’t the YubiKey. This time I did the tried and true method, paper backups, plus an offline USB. I’d already lost one key to the failure mode where the YubiKey is the only copy.
For the transition itself I wrote a short statement saying the old key is superseded and the new one is mine, signed it with both keys, and put it up at asmussen.tech/key-transition-2026-06-08.md. Anyone holding the old key can verify the new one directly. The new key went up on hkps://keys.openpgp.org.
Blast radius, fully realized
Then the actual work, use by use.
gopass. Re-encrypted the whole store to the new key. Same recipient, me, just a dead key swapped for a live one. Expired subkeys still decrypt fine, so nothing was lost in the move.
SSH. The auth subkey changed, so the SSH identity changed with it. Regenerated the key, updated GitHub, authorized_keys, and known_hosts wherever my old identity was locked in.
Commits. Swapped the signing key in git config, registered the new GPG key on GitHub.
nixpkgs. My maintainer entry in maintainer-list.nix got the new fingerprint, with the old one kept listed until it expires. That’s PR #530634, merged through the merge queue on Jun 20: 30 checks passed, zero rebuilds, and the commit itself signed with the new key.
The old subkeys expired on 2026-08-09. The old fingerprint is still in nixpkgs, I said I’d remove it in a follow-up PR, and I haven’t opened that one yet. The migration is done. The cleanup isn’t.
What I’d do differently
Write it down. The old key died because I deleted its only backups and then lost access to the certify key. Two years of expiry ticking down, nothing I could do.
The YubiKey is a single point of failure until the key exists somewhere else. Expiry is a hard deadline when the certify key is gone, and a one-liner when it isn’t. The paper backup survives the laptop, the USB, and the YubiKey itself.
This time the subkeys expire and I’ll just extend them. Because I have the certify key. And if I lose that too, the paper is still there.
Reference
New fingerprint. 220D 68EC DF94 20EE 1E6C B215 D92D 668B 77A2 9897, published on hkps://keys.openpgp.org.
Transition statement. https://asmussen.tech/key-transition-2026-06-08.md, signed with the old and the new key.
nixpkgs PR. #530634, merged 2026-06-20.