Fixing Repository GPG Key Errors (NO_PUBKEY)

Fixing Repository GPG Key Errors (NO_PUBKEY)

What You'll Learn

  • Tell apt's GPG error apart by reading NO_PUBKEY vs EXPKEYSIG
  • Fetch the missing public key into a keyring and bind it with signed-by=
  • Move off the deprecated apt-key to the current key-management model

Quick Summary

  • NO_PUBKEY <KEYID> -> the public key is not on your machine. Fetch it into a keyring
  • EXPKEYSIG / KEYEXPIRED -> the signing key expired. Re-fetch the current key
  • The safe pattern is fetch the HTTPS key URL -> gpg --dearmor -> place in /etc/apt/keyrings/ -> signed-by=

Assumptions

  • OS: Ubuntu 20.04+ / Debian 11+
  • You have sudo
  • Target is a third-party APT repository (for the official archive key, see the dedicated section)

What is a GPG key error and why does it happen?

Conclusion: APT verifies each repository's signature with a public key. If the key is missing, expired, or rotated, apt update raises a GPG error.

APT checks, on every run, whether a repository's Release file is signed by a trusted key (secure apt). When verification fails, it refuses the update and prints a warning. The causes fall into three groups.

Message Meaning Common cause
NO_PUBKEY <KEYID> No matching public key locally Key never added, or it was removed
EXPKEYSIG <KEYID> Signature is from the key, but the key expired Vendor's signing key passed its expiry
KEYEXPIRED <unixtime> The key's validity period has passed Same as above (gpg's wording)

A typical output looks like this:

W: GPG error: https://repo.example.com stable InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 871920D1991BC93C
E: The repository 'https://repo.example.com stable InRelease' is not signed.

Silencing the warning by disabling verification (trusted=yes or --allow-unauthenticated) means you risk installing tampered packages. As a rule, fix it by installing the correct key.

How do you fix a NO_PUBKEY error?

Conclusion: Note the KEYID in the message, then fetch the vendor's public key into a keyring. If the vendor publishes the key over HTTPS, that URL is the safest source.

1. Identify the missing key ID

The 16-digit (or 8-digit) hex value after NO_PUBKEY is the key ID. Above, it is 871920D1991BC93C.

Most vendors publish a .gpg or .asc public key over HTTPS. Convert it to a binary keyring with gpg --dearmor and place it under keyrings.

$ curl -fsSL https://repo.example.com/gpg.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg

--dearmor converts the ASCII form (-----BEGIN PGP PUBLIC KEY-----) into a binary keyring. If the file is already binary .gpg, save it directly with curl -fsSL ... -o /etc/apt/keyrings/example.gpg.

3. Add signed-by to the sources entry

Use signed-by= to state which repository this key verifies (see details).

$ echo "deb [signed-by=/etc/apt/keyrings/example.gpg] https://repo.example.com stable main" \
  | sudo tee /etc/apt/sources.list.d/example.list
$ sudo apt update

When you only know the key ID: fetch from a keyserver

If you do not have the vendor URL but know the key ID, receive it straight into a keyring from a keyserver.

$ sudo gpg --no-default-keyring \
  --keyring /etc/apt/keyrings/example.gpg \
  --keyserver keyserver.ubuntu.com \
  --recv-keys 871920D1991BC93C

A keyserver fetch cannot prove the key ID truly belongs to the vendor. Prefer the vendor's HTTPS URL whenever it is available.

How do you fix an expired signature (EXPKEYSIG / KEYEXPIRED)?

Conclusion: An expired signature is not a missing key - it is the same key past its expiry. Re-fetch the vendor's updated public key and overwrite the keyring.

The output looks like this:

W: GPG error: https://repo.example.com stable InRelease: The following signatures were invalid: EXPKEYSIG 871920D1991BC93C Example Repo Signing Key

The fix is almost identical to NO_PUBKEY: overwrite the keyring with the latest key. Vendors usually republish the same key with an extended expiry, or a successor key.

# Re-fetch and overwrite the public key
$ curl -fsSL https://repo.example.com/gpg.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg
$ sudo apt update

Check the expiry of the fetched key here - look for [expired] or an expires: line.

$ gpg --show-keys /etc/apt/keyrings/example.gpg

If the vendor has not refreshed the key yet, you cannot fix it from your side. Check the vendor's announcement or repository instructions page.

Should you avoid apt-key?

Conclusion: apt-key is deprecated on Ubuntu 20.04 / Debian 11 and later. Do not use it in new procedures - move to keyring files plus signed-by=.

You will see old guides using apt-key adv --recv-keys ..., but apt-key is slated for removal and has a design flaw: it adds the key to a single keyring trusted by all repositories (one vendor's key can then verify another vendor's packages).

The current answer is to keep keys in separate files and scope each with signed-by= so it only applies to the intended repository.

Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).

When you see this warning, migrate to the method in the next section.

How do you place the key correctly with signed-by?

Conclusion: Put the key under /etc/apt/keyrings/ and give its full path in the sources entry's [signed-by=...]. The file needs read permission of 644 or more.

Location and permissions

Item Recommended
Key location /etc/apt/keyrings/ (create with sudo install -d -m 0755 /etc/apt/keyrings)
Key format binary, produced by gpg --dearmor
File permission 644 or more (readable by non-root)

If the keyring file is 600 (root-only), the _apt user cannot read the key and NO_PUBKEY will not go away. Verify with sudo chmod 644 /etc/apt/keyrings/example.gpg.

Sources entry (one-line format)

deb [signed-by=/etc/apt/keyrings/example.gpg] https://repo.example.com stable main

Sources entry (deb822 format / .sources)

Recent Ubuntu / Debian also accept the deb822 format. Put the same path in Signed-By:.

$ sudo tee /etc/apt/sources.list.d/example.sources > /dev/null <<'EOF'
Types: deb
URIs: https://repo.example.com
Suites: stable
Components: main
Signed-By: /etc/apt/keyrings/example.gpg
EOF

What if the official Ubuntu repository key is expired?

Conclusion: The official archive keys are managed by the ubuntu-keyring (or debian-archive-keyring) package. Updating it installs the latest keys.

If the error is on the official Ubuntu/Debian repository rather than a third party, try refreshing the keyring package.

# Ubuntu
$ sudo apt install --reinstall ubuntu-keyring

# Debian
$ sudo apt install --reinstall debian-archive-keyring

If apt update itself is failing on the key error, it may be unable to download the above. In that case, check the vendor's key-rotation announcement, obtain the new *-keyring package (.deb) over a trusted channel, and install it with sudo dpkg -i.

Troubleshooting cheat sheet

Conclusion: The message text decides the cause. NO_PUBKEY means fetch, EXPKEYSIG means re-fetch, and check permissions and paths last.

Symptom Check Fix
NO_PUBKEY <KEYID> Note the key ID Fetch from vendor URL or keyserver into the keyring
EXPKEYSIG / KEYEXPIRED gpg --show-keys for expiry Overwrite the keyring with the latest key
Added the key but no change ls -l /etc/apt/keyrings/ Set permission to 644+ (chmod 644)
Added the key but no change signed-by= path in sources Match the keyring's full path
apt-key is deprecated - Migrate to keyring + signed-by=
Happens on official repo - Reinstall ubuntu-keyring / debian-archive-keyring

Next Reading