Troubleshooting

apt GPG error NO_PUBKEY — add the repository key the modern way

'The following signatures couldn't be verified … NO_PUBKEY': apt lacks the repo's key. Add it to /etc/apt/keyrings with signed-by — apt-key is deprecated.

Published ~9 min read

The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY … means apt has a repository configured but does not hold the key that repository signs its index with. The fix is to download the vendor’s signing key into /etc/apt/keyrings/, point the repository entry at it with signed-by, and run apt update again. Do not reach for apt-key: it is deprecated and has been removed from Debian 13.

quick fix (Debian · Ubuntu)
sudo apt install -y curl gpg          # minimal images may lack gpg
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://repo.example.com/key.gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg

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

sudo apt update

What does the error mean?

apt verifies the signature on every repository’s release index before it trusts a single package from it. NO_PUBKEY is the case where the signature is present but the matching public key is not installed, so apt cannot check it and refuses the repository. Two details in the message tell you exactly what to add:

  • The key ID — the 16-hex-digit value after NO_PUBKEY. It identifies the key you are missing.
  • The repository URL — the line names which repository failed, so you know whose key to fetch. The key always comes from the vendor’s own documentation, never from a stranger who happens to publish that ID.

On Debian 13 the wording is different because apt 3.0 verifies with the Sequoia toolkit (sqv) instead of GnuPG. A missing key reads Sub-process /usr/bin/sqv returned an error code (1) … Missing key …, which is needed to verify signature, and the repository is reported as not signed. The cause and the fix are the same.

How do I add the key correctly?

The modern method puts each vendor’s key in its own file under /etc/apt/keyrings/ and binds it to one repository with signed-by, so the key can only vouch for that repository and nothing else. The key file must be in binary (dearmored) form if the vendor ships it as ASCII text — that is what gpg --dearmor does. If the downloaded file is already binary (it does not start with -----BEGIN PGP PUBLIC KEY BLOCK-----), save it as it is instead.

1 · fetch and dearmor the key
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://repo.example.com/key.gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/example.gpg

Then reference it. Both the one-line format and the newer deb822 .sources format work on Debian 12 and 13 and Ubuntu 22.04 and 24.04 — pick one, not both:

2a · one-line .list
echo "deb [signed-by=/etc/apt/keyrings/example.gpg] https://repo.example.com/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/example.list
2b · deb822 .sources (preferred on Debian 13)
sudo tee /etc/apt/sources.list.d/example.sources >/dev/null <<'EOF'
Types: deb
URIs: https://repo.example.com/apt
Suites: stable
Components: main
Signed-By: /etc/apt/keyrings/example.gpg
EOF

Before you trust it, check that the key is the one the vendor publishes. Compare the fingerprint against their documentation:

3 · verify the fingerprint, then update
gpg --show-keys /etc/apt/keyrings/example.gpg
# match the fingerprint against the vendor's published value, then:
sudo apt update

What if the vendor only gives a key ID?

Some projects publish a key ID or fingerprint rather than a download URL. Fetch the key from a keyserver, export it into a keyring file, and — this part is not optional — compare the fingerprint against the vendor’s page before you use it:

from a keyserver
KEYID=0xABCDEF0123456789          # the full fingerprint is safer than a short ID
sudo apt install -y gnupg          # --recv-keys needs dirmngr, which gnupg brings
sudo install -d -m 0755 /etc/apt/keyrings

export GNUPGHOME=$(mktemp -d)      # throwaway keyring, keeps ~/.gnupg clean
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys "$KEYID"
gpg --export "$KEYID" | sudo tee /etc/apt/keyrings/vendor.gpg >/dev/null
rm -rf "$GNUPGHOME"; unset GNUPGHOME

gpg --show-keys /etc/apt/keyrings/vendor.gpg   # confirm the fingerprint

How do I fix: Key is stored in legacy trusted keyring (/etc/apt/trusted.gpg)?

This warning means a key was added the old way, into the shared /etc/apt/trusted.gpg keyring (usually by an old apt-key add command), where it is trusted for every repository on the system. Move that one key into its own file and bind it with signed-by:

migrate one key out of trusted.gpg
# list the keys in the legacy keyring and find the right fingerprint
sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --list-keys

# export just that key into its own file
sudo install -d -m 0755 /etc/apt/keyrings
sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg \
  --export <FINGERPRINT> | sudo tee /etc/apt/keyrings/vendor.gpg >/dev/null

# remove it from the shared keyring
sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg \
  --batch --yes --delete-keys <FINGERPRINT>

Then add [signed-by=/etc/apt/keyrings/vendor.gpg] to that repository’s entry. The simplest route, if you can, is often to remove the repository file altogether and re-add it the modern way from the vendor’s current instructions.

What if the missing key is Debian’s or Ubuntu’s own?

When the key that is missing belongs to the distribution itself — not a third party — reinstall the distribution’s keyring package:

restore the archive keyring
sudo apt install --reinstall debian-archive-keyring     # Debian
sudo apt install --reinstall ubuntu-keyring              # Ubuntu

This commonly appears after a release upgrade that pulled in a repository for the new release before its key was present. One lookalike to rule out first: if the message is about a signature that is not valid yet rather than a missing key, the server’s clock is wrong, not the keyring. Check it with timedatectl and fix the time before touching keys.

What does EXPKEYSIG mean?

EXPKEYSIG is a related but different error: the key is installed, but it (or its signing subkey) has passed its expiry date, so apt will not trust new signatures made with it. The vendor rotates or extends the key and publishes the new one — download it again with the same steps above, replacing the file in /etc/apt/keyrings/. There is no safe way to “ignore” an expired key; get the current one.

Is Debian 13 stricter?

Yes. apt 3.0 on Debian 13 verifies signatures with Sequoia’s sqv, which enforces a cryptographic policy and rejects keys and signatures that use algorithms it considers too weak. Its default policy phases out several of them on fixed dates — for example it stops accepting SHA-1 self-signatures and SHA-224 from February 2026, and DSA keys were cut off earlier. A repository whose key is still signed with an old algorithm fails on Debian 13 even though the same key worked on Debian 12. The real fix is to get an updated key from the vendor. sudo apt --audit update shows the policy warnings for every configured repository, and the policy file at /usr/share/apt/default-sequoia.config lists the cut-off dates if you need to understand a specific rejection. A new Debian VPS installed with 13 has this apt from the first boot.

How do I fix “Public key for … .rpm is not installed” on Rocky or Alma?

The RHEL family has the same problem with a different message. dnf downloads the package, then reports Public key for example.rpm is not installed and GPG check FAILED because it has no key to verify it with. Import the vendor’s key and let the .repo file reference it:

Rocky · Alma · RHEL
# import the key directly
sudo rpm --import https://repo.example.com/RPM-GPG-KEY-example

# or name it in the .repo file so dnf imports and verifies it
sudo tee /etc/yum.repos.d/example.repo >/dev/null <<'EOF'
[example]
name=Example
baseurl=https://repo.example.com/rhel/$releasever/$basearch
enabled=1
gpgcheck=1
gpgkey=https://repo.example.com/RPM-GPG-KEY-example
EOF
sudo dnf install -y example

Should I just remove the repository?

If it is one you no longer want, yes — that is the cleanest fix, rather than chasing its key. Delete its file from /etc/apt/sources.list.d/ (or /etc/yum.repos.d/) and run the update again.

Adding a third-party repository is exactly the kind of change worth being able to undo. On Standard KVM you can take a panel snapshot before you start and roll back in seconds if a new repository drags in a package that breaks something. If this came up while adding a PPA or a repository by hand, the companion piece is add-apt-repository: command not found, which uses the same keyring method; and if apt then cannot find the package at all, see Unable to locate package.

Frequently asked

Has apt-key been removed?

It is deprecated and gone from Debian 13, which has no apt-key binary at all. It still ships on Debian 12 and Ubuntu 22.04/24.04 but prints a deprecation warning and should not be used. The replacement everywhere is a per-repository key file under /etc/apt/keyrings/ referenced with signed-by, which trusts the key for one repository instead of all of them.

Where should repository keys go?

In their own file under /etc/apt/keyrings/ (create it with 'install -d -m 0755'), dearmored to binary with 'gpg --dearmor' if the vendor ships ASCII, and bound to the one repository with [signed-by=/etc/apt/keyrings/vendor.gpg] or the Signed-By: field in a deb822 .sources file. Not in the shared /etc/apt/trusted.gpg, which is the legacy keyring apt now warns about.

How do I fix 'Key is stored in legacy trusted keyring'?

Export that one key out of /etc/apt/trusted.gpg into its own file under /etc/apt/keyrings/ ('gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --export <fingerprint>'), delete it from the shared keyring with the same --keyring options and '--delete-keys <fingerprint>', and add [signed-by=...] to the repository entry. Often it is simplest to remove the repository's file and re-add it the modern way from the vendor's current instructions.

Is [trusted=yes] safe?

No. [trusted=yes] does not add the missing key — it turns off signature verification for that repository, so apt installs whatever the server sends without checking it. Add the vendor's key with signed-by instead; it is one extra command and keeps verification on.

Related articles

All troubleshooting