Fixing Repository GPG Key Errors (NO_PUBKEY)
What You'll Learn
- Tell apt's GPG error apart by reading
NO_PUBKEYvsEXPKEYSIG - Fetch the missing public key into a keyring and bind it with
signed-by= - Move off the deprecated
apt-keyto the current key-management model
Quick Summary
NO_PUBKEY <KEYID>-> the public key is not on your machine. Fetch it into a keyringEXPKEYSIG/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 updateraises 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
KEYIDin 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.
2. Fetch from the vendor's key URL (recommended)
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-keyis deprecated on Ubuntu 20.04 / Debian 11 and later. Do not use it in new procedures - move to keyring files plussigned-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(ordebian-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 |
What not to do
- Run with verification disabled (
[trusted=yes]or--allow-unauthenticated) - Import an unknown key ID from a keyserver and trust it globally
- Add a new key with
apt-key add(deprecated, global-trust risk)