You run sudo apt update on your Ubuntu or Debian VPS and, instead of a clean refresh, apt complains about a GPG error: NO_PUBKEY, "The repository is not signed", or a yellow warning about a "legacy trusted.gpg keyring". Updates for that repository stop, and sometimes the whole apt upgrade refuses to go ahead. It looks scary, but it's almost always a key-management problem and not a compromised server. Here's how to read the error, fix it properly, and stop it coming back.
Symptom: what the errors actually look like
You'll usually see one (or a mix) of these at the end of apt update:
W: GPG error: https://download.docker.com/linux/ubuntu noble InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8
E: The repository 'https://download.docker.com/linux/ubuntu noble InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
W: https://packages.example.com/apt/dists/stable/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.
W: An error occurred during the signature verification ... EXPKEYSIG 1234ABCD5678EF90 Vendor Repo Signing Key
Each one has a different cause, so it's worth knowing which you've got before you start pasting commands.
Cause: why apt doesn't trust the repository
Every apt repository signs its Release/InRelease file with a GPG key. Your server keeps the matching public keys locally. If apt can't find the key, or the key it finds has expired, it refuses to use that repo. The common reasons:
| Error | What it really means | Typical trigger |
|---|---|---|
NO_PUBKEY <ID> | The signing key isn't on your server, or isn't where the repo entry says it is | Repo added without its key, vendor rotated keys, wrong signed-by path |
EXPKEYSIG <ID> | The key is there, but it has expired | Vendor extended or replaced the key; your copy is old |
Legacy trusted.gpg keyring | Key was added with the deprecated apt-key add | Old install guides, servers upgraded from Ubuntu 20.04 or Debian 11 |
Conflicting values set for option Signed-By | The same repo is defined twice with different keys | Running a vendor's install script more than once |
Release file ... is not valid yet | Your server clock is behind | Clock drift, no NTP after a restore or snapshot rollback |
Fix: step by step
Step 1: Find the repo that's failing
The URL in the error tells you which repository it is. Now find where it's defined:
grep -rn "download.docker.com" /etc/apt/sources.list /etc/apt/sources.list.d/
On Ubuntu 24.04 and Debian 12+ you'll often see .sources files (the newer deb822 format) next to the old one-line .list files. Both work, and apt reads both.
Step 2: Re-download the key from the vendor (the right way)
The modern approach is one key file per repository, stored in /etc/apt/keyrings/, with the repo entry pointing at it through signed-by. That way a key only vouches for its own repo, not your entire system.
Using Docker as the example (swap in your vendor's key URL from their official docs):
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Then make sure the repo line references that file. A one-line .list entry looks like this:
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable
Or, in deb822 .sources format:
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: noble
Components: stable
Signed-By: /etc/apt/keyrings/docker.gpg
Run sudo apt update again. If the error's gone, you're done.
Step 3: No vendor URL? Pull the key by its ID
Sometimes the vendor doesn't publish a direct key URL, or their docs are out of date. You can fetch the exact key ID from the error via a keyserver, then export it into a keyring apt can read:
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8
gpg --export 7EA0A9C3F273FCD8 | sudo tee /etc/apt/keyrings/vendor.gpg > /dev/null
sudo chmod a+r /etc/apt/keyrings/vendor.gpg
Before you trust it, check the fingerprint against the vendor's website:
gpg --show-keys /etc/apt/keyrings/vendor.gpg
Don't create the keyring withgpg --no-default-keyring --keyring /etc/apt/keyrings/x.gpg --recv-keys. Newer GnuPG writes that file in "keybox" format, which apt can't read, and you'll getNO_PUBKEYagain even though the key is technically there. Runfile /etc/apt/keyrings/x.gpg: if it says "GPG keybox database", re-export it as shown above.
Step 4: Migrate keys out of the legacy trusted.gpg
The "legacy trusted.gpg keyring" message is only a warning for now, but apt-key itself has been removed in newer releases like Debian 13, so it's worth cleaning up before your next OS upgrade. List what's in there:
gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --list-keys
Export the key for the repo that's complaining (use the last 8 characters of its fingerprint or the full fingerprint) into its own file:
gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --export F273FCD8 | sudo tee /etc/apt/keyrings/vendor.gpg > /dev/null
Add signed-by=/etc/apt/keyrings/vendor.gpg to that repo's entry, run apt update, and once everything's quiet you can delete the old key:
sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --delete-keys F273FCD8
Step 5: Clean up duplicate repo entries
If you see Conflicting values set for option Signed-By, the repo is listed twice. Find both copies:
grep -rn "repo-url-here" /etc/apt/sources.list /etc/apt/sources.list.d/
Keep one file, delete the other, and make sure the survivor has the correct signed-by path.
Step 6: Rule out the clock
"Not valid yet" or signatures that suddenly look invalid right after a snapshot restore are usually a time problem, not a key problem:
timedatectl
sudo timedatectl set-ntp true
If the clock's way off, fix time sync first, then re-run apt update.
What not to do
- Don't add
[trusted=yes]to the repo line. It makes the error go away by switching off signature checks, so anyone who can tamper with that traffic can feed you packages. - Don't use
apt-key adv --recv-keysfrom old tutorials. It drops the key into the global keyring, so it can vouch for every repo on the box. - Don't ignore it for months. A disabled repo means no security updates for that software. Docker, PHP, Node.js, and MariaDB repos are the usual offenders.
Prevention
- Follow the vendor's current install docs when you add a repo. Most have moved to the
/etc/apt/keyrings+signed-bypattern. - Keep a short note of every third-party repo on the server, with its key file name. When one fails you'll know straight away where to look.
- Turn on unattended upgrades or at least a weekly
apt updatecheck, so you catch a rotated key in days, not months. - Before a major OS upgrade (say, Ubuntu 22.04 to 24.04), run
apt updateand clear every warning first. Third-party repos get disabled during release upgrades, and stale keys make re-enabling them painful. - Take a VPS snapshot before big package changes, so a bad repo change is a two-minute rollback.
Frequently Asked Questions
Does a NO_PUBKEY error mean my VPS has been hacked?
Almost never. It means apt can't find the key it needs to verify a repository. That's a configuration problem, usually from a missing or rotated key. The risk only appears if you "fix" it by turning off verification.
Can I still install packages while one repo shows a GPG error?
Yes. apt disables the broken repo and keeps using the rest, so Ubuntu or Debian's own packages still update. You just won't get anything from the broken repo until you fix its key.
What's the difference between /etc/apt/keyrings and /etc/apt/trusted.gpg.d?
Keys in /etc/apt/trusted.gpg.d/ are trusted for every repository on the system. Keys in /etc/apt/keyrings/ are only used when a repo entry names them with signed-by, which is safer and is the current recommendation.
Should my key file be .gpg or .asc?
Both work with modern apt. .gpg is a binary keyring (what gpg --dearmor produces); .asc is the ASCII-armored text version you'd download as-is. Just make sure the file extension matches the format, and that it isn't a GPG keybox file.
The error came back after I upgraded my OS. Why?
Release upgrades comment out or disable third-party repos, and the repo's suite name (like jammy to noble) often needs updating too. Re-enable the entry, update the codename, confirm the signed-by path still exists, and run apt update.
