DevOps & Workflow19 min read

    Backup and Restore a GPG Key on Linux

    By DanaServer Monitoring & Linux
    Share

    "How do I back up my GPG key?" is the kind of question that has a one-line answer (gpg --export-secret-keys) and a one-day answer (the reason restoring it on a new box doesn't quite work). The literal export is easy. A working restore — one where signed commits still verify, encrypted backups still decrypt, and your own keys aren't flagged "unknown trust" the moment you import them — needs four separate pieces of state out of ~/.gnupg, not one. Most "my GPG broke after migration" stories are some variant of "I exported the secret key and assumed that was enough."

    This guide walks through what's actually inside a GPG keyring, the full backup procedure on Linux, the restore procedure on a new box, how to verify the restore actually works, and the failure modes that catch people the first time. Everything below assumes GnuPG 2.2 or newer (the default on AlmaLinux/Rocky 8+, RHEL 8+, Debian 10+, Ubuntu 20.04+, and any current rolling distro). GPG 1.x is a different keyring format and is called out where it matters.


    What's actually in ~/.gnupg

    A GPG installation stores all its state under ~/.gnupg/. The interesting files, on a modern gpg2 setup:

    ~/.gnupg/
    ├── pubring.kbx                # public keys (and key metadata)
    ├── trustdb.gpg                # ownertrust database — your opinion of each key
    ├── openpgp-revocs.d/          # auto-generated revocation certificates
    │   └── <FINGERPRINT>.rev
    └── private-keys-v1.d/         # secret keys, one file per key, by keygrip
        ├── <KEYGRIP>.key
        └── ...
    

    The structural change you need to know about: since GnuPG 2.1, public keys live in pubring.kbx and secret keys live as individual files in private-keys-v1.d/. The old secring.gpg from gpg 1.x is gone. This matters because copying ~/.gnupg from a gpg1 system to a gpg2 system is not a backup — the secret keys won't be picked up until they're migrated. Always export and re-import; don't trust cp -a ~/.gnupg/.

    For a clean backup-and-restore, you need four logical pieces of state:

    1. Public keys — your own and anyone else's you've imported.
    2. Secret keys — the part that actually decrypts and signs. Without these, the keyring is read-only.
    3. Ownertrust database — your stated trust level for each key (ultimate for your own, full/marginal/unknown for others). Without this, you'll see WARNING: This key is not certified with a trusted signature on every operation, even with your own keys.
    4. Revocation certificate — a pre-signed "this key is revoked" message you can publish if the secret key is ever compromised. You can't generate one after losing the key, so it must exist as part of the backup.

    Each piece has its own export command. Skipping any of them produces a restore that looks fine until it isn't.


    List your keys first

    Before exporting anything, see what's there:

    # Public keys
    gpg --list-keys
    
    # Secret keys — what you can actually export
    gpg --list-secret-keys --keyid-format=long
    

    Typical output for your own key:

    sec   ed25519/AB12CD34EF567890 2024-03-12 [SC]
          9A8B7C6D5E4F32101122334455667788AB12CD34
    uid                 [ultimate] Alice Example <alice@example.com>
    ssb   cv25519/1122334455667788 2024-03-12 [E]
    

    The long hex string on the second line — 9A8B7C6D5E4F32101122334455667788AB12CD34 — is the fingerprint, the only identifier you should use in export commands. Short key IDs (the last 8 or 16 hex chars) collide and have been weaponized in the past; always use the fingerprint when you can.

    If you have multiple secret keys (work, personal, signing-only), decide upfront which you're backing up. The procedures below work for one key at a time; repeat per fingerprint, or use --export-secret-keys with no fingerprint to export all of them (rarely what you want).

    For the rest of this guide, FPR stands in for your fingerprint — substitute the real one.


    The full backup procedure

    Run all four exports for each key you want to migrate. Use --armor (or -a) so the output is ASCII text — easier to inspect, easier to transport, and immune to the kind of binary corruption that silently destroys a key in transit.

    FPR=9A8B7C6D5E4F32101122334455667788AB12CD34   # your fingerprint
    BACKUP=~/gpg-backup-$(date +%Y%m%d)
    mkdir -p "$BACKUP"
    chmod 700 "$BACKUP"
    
    # 1. Public key
    gpg --armor --export "$FPR" > "$BACKUP/public.asc"
    
    # 2. Secret key (includes the master + all subkeys)
    gpg --armor --export-secret-keys "$FPR" > "$BACKUP/secret.asc"
    
    # 3. Ownertrust database (your trust opinions of every key)
    gpg --export-ownertrust > "$BACKUP/ownertrust.txt"
    
    # 4. Revocation certificate
    gpg --armor --gen-revoke "$FPR" > "$BACKUP/revocation.asc"
    

    A few things worth knowing about each step:

    • --export-secret-keys will prompt for the key's passphrase (because the secret material is symmetrically encrypted at rest under that passphrase). The exported file is still encrypted with the same passphrase — cat secret.asc shows armored ciphertext, not the raw key. That's a feature, not a bug; the export is no more dangerous on disk than the live keyring was.
    • --export-ownertrust does not need the passphrase and produces a small plaintext file mapping FPR:trust_level. Lose this and your imported keys will all show as "unknown trust" until you re-run gpg --edit-key <FPR> and trust each one manually. For a keyring with dozens of signers, that's hours of work to redo.
    • --gen-revoke is interactive: it asks for the revocation reason (0 for "no reason specified" is fine for a generic backup cert), an optional description, and the passphrase. The output file lets you revoke the key without having the secret key available — which is exactly the situation you'd be in if the key were stolen or lost. Generate it once, store it somewhere safe and separate, and never look at it again unless you need it.

    Verify the backup is non-empty and looks right:

    ls -la "$BACKUP"
    head -1 "$BACKUP/secret.asc"
    # -----BEGIN PGP PRIVATE KEY BLOCK-----
    head -1 "$BACKUP/public.asc"
    # -----BEGIN PGP PUBLIC KEY BLOCK-----
    head -1 "$BACKUP/revocation.asc"
    # -----BEGIN PGP PUBLIC KEY BLOCK-----     (revocation certs are public-key-block-shaped)
    wc -l "$BACKUP/ownertrust.txt"
    # (small number — one line per key you've assigned trust to)
    

    Now tar the directory, encrypt it again if the storage isn't trusted, and move it off the box:

    tar -czf gpg-backup-$(date +%Y%m%d).tar.gz -C "$(dirname "$BACKUP")" "$(basename "$BACKUP")"
    # Optionally: encrypt the tarball under a symmetric passphrase for transit
    gpg --symmetric --cipher-algo AES256 gpg-backup-$(date +%Y%m%d).tar.gz
    

    The secret-key file is already encrypted with your key passphrase. The --symmetric outer wrap is for defense in depth — it stops anyone who gets the tarball from even seeing which key fingerprint or UID is inside, which is the part the passphrase doesn't protect.


    A complete worked example

    Backing up Alice's GPG keypair end-to-end:

    # Find the fingerprint
    gpg --list-secret-keys --keyid-format=long alice@example.com
    # sec   ed25519/AB12CD34EF567890 2024-03-12 [SC]
    #       9A8B7C6D5E4F32101122334455667788AB12CD34
    
    # Define vars
    FPR=9A8B7C6D5E4F32101122334455667788AB12CD34
    BACKUP=~/gpg-backup-alice-$(date +%Y%m%d)
    mkdir -p "$BACKUP" && chmod 700 "$BACKUP"
    
    # Four exports
    gpg --armor --export "$FPR"             > "$BACKUP/public.asc"
    gpg --armor --export-secret-keys "$FPR" > "$BACKUP/secret.asc"
    gpg --export-ownertrust                 > "$BACKUP/ownertrust.txt"
    gpg --armor --gen-revoke "$FPR"         > "$BACKUP/revocation.asc"
    
    # Sanity check
    ls -la "$BACKUP"
    # -rw-------  public.asc        ~2 KB
    # -rw-------  secret.asc        ~3-5 KB
    # -rw-------  ownertrust.txt    a few hundred bytes
    # -rw-------  revocation.asc    ~2 KB
    
    # Tarball + symmetric outer wrap
    cd ~
    tar -czf gpg-backup-alice.tar.gz "$(basename "$BACKUP")"
    gpg --symmetric --cipher-algo AES256 gpg-backup-alice.tar.gz
    # produces gpg-backup-alice.tar.gz.gpg — this is the artifact to move
    
    # Move it off-box (scp, rsync, USB, whatever your policy says) then:
    shred -u gpg-backup-alice.tar.gz                 # delete the unencrypted tar
    rm -rf "$BACKUP"                                 # delete the plaintext export dir
    

    That .tar.gz.gpg file is the artifact to keep. Store it somewhere durable and not-on-the-same-machine — a password manager that supports file attachments, an encrypted USB drive in a drawer, a backup bucket, all three. The revocation cert is small enough to also print on paper and stash separately; if you ever lose the digital backup and your live key, the printed cert is the only way to tell the keyservers "this key is dead, don't trust signatures from it."


    The restore procedure

    On the new machine, with GnuPG installed and a fresh empty ~/.gnupg/ (or at least without an existing copy of the same key), unwrap the backup and import in the right order:

    # Unwrap
    gpg --decrypt gpg-backup-alice.tar.gz.gpg > gpg-backup-alice.tar.gz
    tar -xzf gpg-backup-alice.tar.gz
    cd gpg-backup-alice-*
    
    # Import in this order: secret first, then ownertrust
    gpg --import secret.asc
    gpg --import-ownertrust ownertrust.txt
    

    Notes:

    • You don't need to import public.asc separately if you imported secret.asc — gpg embeds the public key inside the secret-key armor and registers both on import. Keep the public export anyway; it's the file you'd hand to someone else or upload to a keyserver, and it makes the backup self-describing if you ever need to recover only the public side.
    • --import-ownertrust is silent. It prints nothing on success. Verify with gpg --list-keys and look for [ultimate] next to your own UID. If it shows [unknown], the import didn't take.
    • Don't import the revocation certificate. Importing it would mark your own key as revoked on this machine. The revocation file is a "break glass" artifact — you only ever send it to a keyserver (via gpg --keyserver hkps://keys.openpgp.org --send-keys $FPR after importing the revoked key) when you're actively retiring the key.

    The new ~/.gnupg/ should now contain the imported secret key:

    gpg --list-secret-keys --keyid-format=long
    # sec   ed25519/AB12CD34EF567890 2024-03-12 [SC]
    #       9A8B7C6D5E4F32101122334455667788AB12CD34
    # uid                 [ultimate] Alice Example <alice@example.com>
    # ssb   cv25519/1122334455667788 2024-03-12 [E]
    

    [ultimate] next to the UID is the visual confirmation that ownertrust came across. If it says [unknown], re-run the ownertrust import.


    Verifying the restore actually works

    A restore that looks fine in --list-secret-keys can still be broken in subtle ways — the secret key might be a stub (because the original was on a smart card), the passphrase agent might not be set up, or the trust import might have silently failed. Two quick tests cover the failure modes:

    # Test 1: sign and verify a message
    echo "restore-test-$(date +%s)" | gpg --clear-sign --local-user "$FPR" > /tmp/sig.txt
    gpg --verify /tmp/sig.txt
    # Should print "Good signature from Alice Example <alice@example.com>"
    # without any "WARNING: This key is not certified" line.
    
    # Test 2: encrypt to yourself and decrypt
    echo "encrypt-test" | gpg --encrypt --armor --recipient "$FPR" > /tmp/enc.asc
    gpg --decrypt /tmp/enc.asc
    # Should print "encrypt-test" (after prompting for the passphrase if not cached)
    

    If Test 1 prints the "WARNING: This key is not certified with a trusted signature" line, your ownertrust import didn't apply — re-run gpg --import-ownertrust ownertrust.txt. If Test 2 fails with decryption failed: No secret key, the secret import didn't take or you imported a stub; see the smart-card section below.


    Subkeys and the "laptop key" pattern

    A common upgrade once the basic backup-and-restore works: keep your master key offline (in a vault, on an air-gapped machine, on encrypted offline media) and only put subkeys on day-to-day laptops and servers. That way, compromise of any one machine leaks the subkeys (which can be revoked individually) but not the master (which is what proves you are you).

    gpg supports this directly:

    # On the secure machine: export only the secret subkeys
    gpg --armor --export-secret-subkeys "$FPR" > laptop-subkeys.asc
    

    Import on the laptop:

    gpg --import laptop-subkeys.asc
    gpg --list-secret-keys --keyid-format=long "$FPR"
    # sec#  ed25519/AB12CD34EF567890 2024-03-12 [SC]
    #       9A8B7C6D5E4F32101122334455667788AB12CD34
    # ssb   cv25519/1122334455667788 2024-03-12 [E]
    

    The sec# (note the #) indicates the master secret is not present on this machine — only the subkeys are. The laptop can still sign and encrypt with the subkeys, but it can't sign new subkeys or alter the master's UIDs. If the laptop is stolen, you revoke just those subkeys from the master and issue new ones; the master identity survives.

    This is the right pattern for any machine you don't fully trust: build servers, CI runners, laptops you travel with, shared workstations. The full master backup (the one with --export-secret-keys) lives on the offline media only.


    Smart cards and YubiKeys: what the export actually contains

    If your secret key lives on a YubiKey, Nitrokey, or other OpenPGP smart card, gpg --export-secret-keys does not export the secret material — the whole point of the smart card is that the secret never leaves it. What you get instead is a stub: a pointer file that says "the secret for this key lives on smart card with serial XYZ."

    gpg --armor --export-secret-keys "$FPR" > secret.asc
    gpg --list-secret-keys "$FPR"
    # sec>  ed25519/AB12CD34EF567890   <-- '>' means "on smart card"
    

    The sec> marker means the secret-key file on disk is a stub. Importing that stub on another machine gives you a keyring that looks like it has the secret key, but every signing or decryption operation will fail with gpg: signing failed: No secret key unless the same smart card is plugged in.

    For smart-card-backed keys, the backup story is different:

    • The on-card secret material is the only copy by design — there is no software backup of it, ever. If the card dies, you cannot recover that key; you revoke it (using the revocation cert you generated when you provisioned the card) and issue a new one.
    • What you do back up for a smart-card key: the public key, the ownertrust, the revocation certificate, and the stub secret-key file (so a new machine knows the keys exist and where they live).
    • Some people keep a "master key" off-card, generate signing/encryption subkeys on it, and keytocard the subkeys onto the YubiKey. In that setup, the master key has a normal software backup (the four-piece procedure above), and the subkeys live on the card with no software backup.

    If you're not on a smart card, you can skip this section. If you are, the takeaway is: a stub is not a key. Don't assume --export-secret-keys saved you from the "card died" scenario.


    Securing the backup

    The encrypted backup is a small file, but it's also the only thing standing between someone who steals it and the ability to impersonate you forever (or read every backup you've ever encrypted with that key). Treat it accordingly.

    • Passphrase strength matters more than at any other time. The backup is at rest on whatever media you put it on, and someone with the file has unlimited offline attempts at the passphrase. A short or guessable passphrase is the same as no encryption. Use a long, random, memorable passphrase — 6+ random words from a list — or store a long random string in a password manager and reference it from there. Diceware is the canonical recipe.
    • At least one copy should be offline. Cloud backup is fine for convenience, but the threat model includes "your cloud account is compromised." Offline media — an encrypted USB drive, an SD card in a safe — defeats that threat.
    • Geographic separation is worth doing. One copy at home, one in a safety-deposit box or with a trusted contact. The fire-and-flood scenario is real and the marginal cost of a second USB is trivial.
    • Paper backups for the revocation cert are worth considering. The cert is small (a few KB of ASCII), prints onto a single sheet, and survives any digital catastrophe. paperkey does the same for the secret key itself — outputs only the secret bits in a checksum-friendly format you can OCR back from a printout.
    • Shamir Secret Sharing (e.g. via ssss-split) splits the backup passphrase across N pieces requiring K to reconstruct. Useful if no single person should be able to use the backup alone — common in team or estate-planning setups.

    For monitored servers and operational keys, treat the backup the same as you'd treat the root SSH key for those servers: as the credential that opens the door to everything those keys touch. If you're tracking sensitive admin credentials in Xitoring's server monitoring, tag the box that holds the live key and alert on unusual access patterns — most "the key got stolen" stories have an audit-log signal at the moment of compromise, if anyone's reading the logs.


    Common mistakes

    The classic ways the backup-and-restore loop quietly breaks:

    Exporting only the secret key

    Skipping the ownertrust export gives you a working keyring on the new machine where every operation prints WARNING: This key is not certified with a trusted signature. Functionally usable for signing but visually broken, and your own key shows as [unknown] instead of [ultimate]. Fix: always include --export-ownertrust.

    Skipping the revocation certificate

    A revocation cert generated before the key is lost is the only way to tell keyservers "stop trusting signatures from this fingerprint." A cert generated after loss is impossible — it requires the secret key. Fix: generate the cert at backup time, store it separately from the secret key, never look at it unless you need it.

    Copying ~/.gnupg/ between machines instead of exporting

    cp -a ~/.gnupg/ /backup/ looks tempting and is wrong for two reasons. First, gpg-agent keeps the keyring locked while running — you may copy a half-written file. Second, the keyring format has changed across gpg versions (notably gpg1 → gpg2.1), and a raw filesystem copy from one to the other will not produce a working keyring on import. Fix: always export to ASCII-armored files; the export format is the stable cross-version interchange format.

    Wrong permissions on ~/.gnupg after restore

    If the imported keyring directory is group- or world-readable, gpg will warn or refuse on every operation:

    gpg: WARNING: unsafe ownership on homedir '/home/alice/.gnupg'
    

    Fix:

    chmod 700 ~/.gnupg
    chmod 600 ~/.gnupg/*
    chmod 700 ~/.gnupg/private-keys-v1.d ~/.gnupg/openpgp-revocs.d 2>/dev/null
    

    Using short key IDs instead of fingerprints

    gpg --export AB12CD34 and gpg --export AB12CD34EF567890 look equally valid but the short form is collidable — multiple unrelated keys can share the same 8 or 16 hex chars, and historically that's been used to trick people into importing the wrong key. Fix: always use the full 40-character fingerprint in export and import commands, especially in scripts.

    Forgetting the passphrase

    The exported secret-key file is encrypted with the original key's passphrase. Restore on a new machine and the first sign/decrypt operation will prompt for that passphrase. If you don't have it, the backup is just ciphertext — there's no recovery. Fix: when you set the passphrase, store it in a password manager at the same time, alongside the backup file.

    Restoring on a system with an existing key for the same UID

    If ~/.gnupg already has a key for the same email address (e.g. one auto-generated by a tool, or an old key that was never cleaned up), the import succeeds but gpg may keep using the old key as the default. Fix: before importing, run gpg --list-secret-keys and delete any stale keys for the same UID with gpg --delete-secret-and-public-key <OLD_FPR> (note: this is destructive — make sure the old key is genuinely stale).

    Mixing gpg1 and gpg2 on the same machine

    Some distros still ship gpg as gpg1 with gpg2 as a separate binary; others have unified them. gpg --version is the only authoritative check. If you exported from one and imported on the other, the keyring may be in two different places (~/.gnupg/secring.gpg vs ~/.gnupg/private-keys-v1.d/). Fix: run the migration utility — gpg --import-options import-local-sigs and, if needed, gpg --list-secret-keys from gpg2 will trigger a one-time migration of the gpg1 keyring.


    Troubleshooting

    • gpg: decryption failed: No secret key — the secret-key import didn't take, or you imported a smart-card stub instead of the real key. Run gpg --list-secret-keys --keyid-format=long $FPR and look for the sec line: sec means a usable secret, sec# means the master is off-machine (subkeys only), sec> means it's a smart-card stub. The fix depends on which you see.
    • gpg: signing failed: Inappropriate ioctl for device — gpg-agent can't find a way to prompt you for the passphrase in a non-TTY context (CI, cron, ssh without -t). Set export GPG_TTY=$(tty) in the calling shell, or configure pinentry-mode loopback in ~/.gnupg/gpg.conf and pass the passphrase via --passphrase-fd (use carefully — passphrase ends up in process environment).
    • gpg: WARNING: This key is not certified with a trusted signature — ownertrust didn't import or wasn't generated for this key. Run gpg --import-ownertrust /path/to/ownertrust.txt, or gpg --edit-key $FPRtrust5 (ultimate) → quit for keys you own.
    • gpg: key X marked as ultimately trusted but signatures still warn — gpg-agent is caching a stale trust evaluation. gpg --check-trustdb rebuilds it; on persistent issues, rm ~/.gnupg/trustdb.gpg and re-import ownertrust.
    • The restore appears to succeed but the wrong key signs — multiple secret keys are present and the default is wrong. Set default-key <FPR> in ~/.gnupg/gpg.conf, or pass --local-user $FPR explicitly to sign commands.
    • gpg: WARNING: unsafe ownership on homedir~/.gnupg or files inside it are group/world readable. chmod 700 ~/.gnupg && chmod 600 ~/.gnupg/* resolves it (with chmod 700 on the subdirectories).
    • Imported keyring on a server but git/SSH signing still fails — the gpg binary used by git may not be the same as your shell's gpg. Check git config --get gpg.program and confirm it points to gpg2. For SSH agent-based signing, the gpg-agent socket also needs to be enabled (enable-ssh-support in ~/.gnupg/gpg-agent.conf).
    • Backup tarball decrypts but contains 0-byte files — the --armor export was redirected before gpg finished writing. Re-run the export; never trust an export that completed in milliseconds.

    Summary

    To back up and restore a GPG key on Linux:

    1. A "GPG backup" is four things, not one. Public key, secret key, ownertrust database, revocation certificate. Skipping any of them gives you a restore that looks fine but is subtly broken.
    2. Use ASCII-armored exports (--armor), not a raw copy of ~/.gnupg/. The export format is the stable, cross-version interchange; the on-disk format is not.
    3. Always identify keys by full fingerprint, not short key ID. Short IDs collide.
    4. Restore order: secret key first, then ownertrust. The secret-key file embeds the public key, so a separate public-key import isn't required.
    5. Verify with a real sign-and-verify and a real encrypt-and-decrypt. --list-secret-keys can show a usable-looking keyring that doesn't actually work.
    6. Smart-card-backed keys export a stub, not the secret. The on-card material has no software backup by design — plan revocation, not recovery.
    7. The revocation certificate must exist before you need it. You can't generate one after losing the secret key.
    8. At least one backup copy lives offline. Cloud + offline + geographic separation is the boring, correct answer.
    9. Treat the passphrase like the root credential it effectively is. Long, random, stored in a password manager at the moment you set it.

    The mechanics are four short commands. The discipline — generating the revocation cert at backup time, keeping the master off the laptop, storing the backup in two places, verifying the restore — is what separates a backup you can rely on at 2am from one you discover doesn't work the moment you actually need it.