_apt user. The modern fix for all of them is the
same, an armoured key dearmoured into /etc/apt/keyrings/, mode
644, referenced by signed-by= in that one repository's
sources entry. Never apt-key add, and never a key in
/etc/apt/trusted.gpg.
🩺 Find your error
NO_PUBKEY
W: GPG error: https://deb.griffo.io/apt trixie InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 7808B4DD62C41256
E: The repository 'https://deb.griffo.io/apt trixie InRelease' is not signed.apt fetched the index, found a valid signature, and has no matching public key. The key is missing, is in a file apt was not told to look at, or is not in the binary format apt expects. Go to the fix.
“is not signed” on its own
E: The repository 'https://… trixie InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.Same cause as above in most cases. It genuinely means "unsigned" only when the
repository ships no InRelease or Release.gpg at all, check
by opening <repo>/dists/<suite>/InRelease in a browser. For
this repository it is always present, so the key is the problem, not the archive.
Legacy trusted.gpg keyring
W: https://…/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg),
see the DEPRECATION section in apt-key(8) for details.Not fatal yet, and worth fixing anyway: a key in trusted.gpg is trusted
for every repository on the machine, not just the one it belongs to.
That is the whole reason apt-key was deprecated. Move it, as below.
“Release file is not valid yet”
E: Release file for https://… is not valid yet (invalid for another 5h 12min 4s).Not a key problem, your system clock is wrong. Fix the clock
(sudo timedatectl set-ntp true) rather than reaching for
-o Acquire::Check-Valid-Until=false.
✅ The fix that covers all of them
Using this repository as the worked example; substitute your own key URL and host for any other repository.
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://deb.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | sudo gpg --dearmor --yes -o /etc/apt/keyrings/deb.griffo.io.gpg
sudo chmod 644 /etc/apt/keyrings/deb.griffo.io.gpg
echo "deb [signed-by=/etc/apt/keyrings/deb.griffo.io.gpg] https://deb.griffo.io/apt $(lsb_release -sc 2>/dev/null) main" | sudo tee /etc/apt/sources.list.d/deb.griffo.io.list > /dev/null
sudo apt updateinstall -d -m 0755 /etc/apt/keyrings
curl -fsSL https://deb.griffo.io/EA0F721D231FDD3A0A17B9AC7808B4DD62C41256.asc | gpg --dearmor --yes -o /etc/apt/keyrings/deb.griffo.io.gpg
chmod 644 /etc/apt/keyrings/deb.griffo.io.gpg
echo "deb [signed-by=/etc/apt/keyrings/deb.griffo.io.gpg] https://deb.griffo.io/apt $(lsb_release -sc 2>/dev/null) main" | tee /etc/apt/sources.list.d/deb.griffo.io.list > /dev/null
apt updatesudo apt install extrepo
sudo extrepo enable griffo
sudo apt update🧩 About the extrepo option. extrepo is Debian's own tool for external repositories: it writes the sources file and installs the signing key for you, checking the key against signed metadata first. This repository is registered with it as griffo, and the always-free mirror as griffo-free, so on Debian (bookworm, trixie, forky and sid) the commands above are the whole setup.
extrepo sets up the sources file and the key only, so a subscription's credentials still go in /etc/apt/auth.conf.d/deb.griffo.io.conf. Ubuntu is not covered, because extrepo publishes metadata for Debian suites only: there, use the sudo or root commands.
Four details in there do all the work, and each one is a common failure on its own:
gpg --dearmorconverts the armoured (.asc) key into the binary form. Saving an.ascfile named.gpgis the single most common cause of NO_PUBKEY. If you prefer to skip dearmouring, keep the.ascextension and pointsigned-by=at the.ascfile, apt reads either, but the name must match the contents./etc/apt/keyrings/is the directory for third-party keys on current Debian and Ubuntu./usr/share/keyrings/is for keys shipped by packages; do not write there by hand.chmod 644. apt drops privileges to the_aptuser to fetch, so a keyring readable only by root produces NO_PUBKEY even though the file is right there. This one wastes hours becausesudo gpgcan read the file perfectly when you test it.signed-by=scopes the key to this repository alone. Without it the key would either be untrusted or, if you put it intrusted.gpg, trusted for every repository on the system.
🧹 Migrating a key off the legacy keyring
List what is in the old keyring, export the one you want, then remove it:
sudo apt-key list 2>/dev/null | grep -B2 -A2 griffo
sudo gpg --keyring /etc/apt/trusted.gpg --export 7808B4DD62C41256 | sudo tee /etc/apt/keyrings/deb.griffo.io.gpg > /dev/null
sudo chmod 644 /etc/apt/keyrings/deb.griffo.io.gpg
sudo apt-key --keyring /etc/apt/trusted.gpg del 7808B4DD62C41256apt-key list 2>/dev/null | grep -B2 -A2 griffo
gpg --keyring /etc/apt/trusted.gpg --export 7808B4DD62C41256 | tee /etc/apt/keyrings/deb.griffo.io.gpg > /dev/null
chmod 644 /etc/apt/keyrings/deb.griffo.io.gpg
apt-key --keyring /etc/apt/trusted.gpg del 7808B4DD62C41256Then add signed-by= to that repository's sources entry, as above. Keys
in /etc/apt/trusted.gpg.d/*.gpg are still honoured and are not deprecated
in the same way, but they are also system-wide, moving them to
keyrings/ plus signed-by= is still the better end state.
🔍 Verify it worked
# the key apt will actually use, and its fingerprint
gpg --show-keys /etc/apt/keyrings/deb.griffo.io.gpg
# what apt itself thinks of the repository
apt-cache policy
sudo apt update# the key apt will actually use, and its fingerprint
gpg --show-keys /etc/apt/keyrings/deb.griffo.io.gpg
# what apt itself thinks of the repository
apt-cache policy
apt updateThe fingerprint must read
EA0F 721D 231F DD3A 0A17 B9AC 7808 B4DD 62C4 1256. If it does not,
you have imported someone else's key, stop and re-fetch it from
the source.
How these packages are built and signed →
🚫 What not to do
apt-key add: deprecated and removed in newer releases; it also grants the key system-wide trust.[trusted=yes]in a sources entry. This disables signature checking for that repository entirely. It makes the error go away by removing the security the error exists to provide.--allow-unauthenticated: same objection, per command.- Downloading a key over plain HTTP, or from a mirror you did not verify. The key is what makes everything else trustworthy.
🔗 Related
- deb822
.sourceson Debian 13, the modern replacement for the one-linesources.listentry above, where the signing key is named inside the source itself. - apt: 401 Unauthorized, the other error this repository can return, and what it means.
- How every package here is built, what the signature you just imported is actually attesting to.