APT repository keys without apt-key: keyrings and Signed-By

Updated November 2026

The old recipe apt-key adv --keyserver … --recv-keys … no longer works: Debian 13 and Ubuntu 26.04 LTS do not ship apt-key at all. Today you download the vendor’s key over HTTPS, check its fingerprint, save it under /etc/apt/keyrings/, and name that file in the repository entry with Signed-By, so the key is trusted for that one repository only.

Why apt-key went away

apt-key add put every vendor key into one global keyring (/etc/apt/trusted.gpg). Any key in it was trusted for every repository, so a key you added for one small vendor could sign packages that seemed to come from Debian itself. Signed-By ties each repository to its own key file. Debian’s guidance for third-party repositories says the key “MUST NOT be placed in /etc/apt/trusted.gpg.d or loaded by apt-key add”, and that signed-by “MUST point to a file, and not a fingerprint”.

apt-key status by release

apt-key and trusted.gpg by release, as of November 2026
Releaseapt-keyNotes
Debian 12 “bookworm”Present, deprecatedIts manual says use of apt-key is deprecated “except for the use of apt-key del in maintainer scripts”.
Ubuntu 22.04 LTS “jammy” (APT 2.4)Present, deprecatedAPT 2.4 warns when a repository is only verified by the legacy trusted.gpg keyring.
Ubuntu 24.04 LTS “noble” (APT 2.7/2.8)Present, deprecatedSame warnings; /usr/bin/apt-key is still in the package.
Debian 13 “trixie” (APT 3.0)Removed/etc/apt/trusted.gpg is no longer trusted; .list lines without signed-by are deprecated.
Ubuntu 26.04 LTS “resolute” (APT 3.2)RemovedNo apt-key in the apt package.

The change happened in APT’s 2.9 development series. Version 2.9.15 (November 2024) stopped using apt-key to verify signatures, and 2.9.17 (December 2024) stopped installing it. Version 2.9.24 (January 2025) removed /etc/apt/trusted.gpg from the trusted path and deprecated entries without Signed-By, which also deprecates /etc/apt/trusted.gpg.d/. APT 3.0 also verifies signatures with Sequoia’s sqv instead of GnuPG’s gpgv on supported Debian platforms.

Stricter key rules in Debian 13. The default policy file shipped with APT 3.0.3 (/usr/share/apt/default-sequoia.config) stops accepting DSA keys (cut-off 2024-02-01), SHA-1 for signatures and version 3 signatures (2026-02-01), Brainpool keys (2028-02-01) and RSA 2048 keys (2030-02-01). A repository signed with an old key can therefore fail on Debian 13 even with a correct Signed-By. Ask the vendor for a current key; do not weaken the policy.

Where keys and sources go

File format rules from apt-secure(8): an ASCII-armored key must end in .asc, a binary key in .gpg. The file must be readable by the _apt system user, so give it mode 0644. Use only letters, digits, _, -, : and . in file names; since APT 2.9.15, other characters such as @ make APT ignore files in the .d directories silently.

Add a repository the modern way

A real example: Docker’s documented setup for Debian. It downloads the key from Docker’s HTTPS site into /etc/apt/keyrings and writes a deb822 file.

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# check the fingerprint before you trust it
gpg --show-keys /etc/apt/keyrings/docker.asc

gpg --show-keys only displays the file; it does not import anything into your keyring. When we ran it in November 2026 it printed:

pub   rsa4096 2017-02-22 [SCEA]
      9DC858229FC7DD38854AE2D88D81803C0EBFCD88
uid                      Docker Release (CE deb) <docker@docker.com>
sub   rsa4096 2017-02-22 [S]

Compare the full 40-character fingerprint with the one in the vendor’s documentation or another source you trust (for example a keyserver lookup by fingerprint, see below). Then create /etc/apt/sources.list.d/docker.sources:

Types: deb
URIs: https://download.docker.com/linux/debian
Suites: trixie
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
sudo apt update

Docker’s own instructions fill Suites and Architectures from /etc/os-release and dpkg --print-architecture; write them out by hand if you prefer. The one-line equivalent, for a docker.list file, is:

deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable

Embedding the key in the .sources file

Since APT 2.4, Signed-By can also hold the armored key itself, so no separate file is needed. apt-secure(8) in Debian 13 says keys “should usually be included” this way. Indent every key line by two spaces and replace the empty line inside the key block with a line holding a single dot:

Signed-By:
  -----BEGIN PGP PUBLIC KEY BLOCK-----
  .
  mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth
  …
  -----END PGP PUBLIC KEY BLOCK-----

A separate file is easier to replace when the vendor rotates its key, and it works with APT older than 2.4.

Converting old instructions

“curl … | sudo apt-key add -”

Save the same file into /etc/apt/keyrings/ instead, check it with gpg --show-keys, and add Signed-By. If the downloaded file is armored (it starts with -----BEGIN PGP PUBLIC KEY BLOCK-----), name it .asc. Debian’s third-party guidance prefers binary files and suggests to “unconditionally dearmor” them; either way works with current APT:

curl -fsSL https://vendor.example/repo/key.asc -o /tmp/vendor.asc
gpg --show-keys /tmp/vendor.asc            # check the fingerprint
gpg --dearmor < /tmp/vendor.asc | sudo tee /etc/apt/keyrings/vendor.gpg > /dev/null
sudo chmod 0644 /etc/apt/keyrings/vendor.gpg

“apt-key adv --keyserver … --recv-keys …”

These commands usually name an old keyserver (often keyserver.ubuntu.com, pgp.mit.edu or the long-gone *.pgp.net pool, see Replacing subkeys.pgp.net) and frequently a short key ID. The safest replacement is to ignore the keyserver and download the key from the vendor’s own HTTPS site, as above. If the vendor publishes only a fingerprint, fetch exactly that full fingerprint into a temporary keyring and export it:

export GNUPGHOME=$(mktemp -d)
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
gpg --list-keys                            # exactly one key, the expected fingerprint?
gpg --export-options export-minimal --export 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 \
  | sudo tee /etc/apt/keyrings/vendor.gpg > /dev/null
sudo chmod 0644 /etc/apt/keyrings/vendor.gpg
gpgconf --kill all; rm -rf "$GNUPGHOME"; unset GNUPGHOME

Never use an 8-digit short key ID for this: different keys can share one. apt-secure(8) recommends --export-options export-minimal when exporting for APT. Older tricks such as gpg --no-default-keyring --keyring FILE --import are noted there as no longer working with recent GnuPG, because GnuPG now creates keybox files that APT cannot read.

Keys already in /etc/apt/trusted.gpg

On a Debian 12 or Ubuntu 24.04 system that you are about to upgrade, move each vendor key out of the global keyring first. GnuPG can read the old file directly:

gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --list-keys
gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --export FINGERPRINT \
  | sudo tee /etc/apt/keyrings/vendor.gpg > /dev/null

Add Signed-By: /etc/apt/keyrings/vendor.gpg to that vendor’s entry, run sudo apt update, and once it works remove the key from the old keyring (on Debian 12, sudo apt-key del FINGERPRINT).

apt modernize-sources

APT 3.0 (Debian 13) has a command that rewrites .list files as .sources files:

sudo apt modernize-sources

It lists the files it would change and asks before writing; answering N makes it a simulation. It keeps the old files as .list.bak and adds Signed-By where it can work it out. In our test it copied an existing signed-by= value correctly, but for a line without one it wrote an empty Signed-By: and printed “Could not determine Signed-By”. It also says it supports only the signed-by and trusted options; our arch=amd64 option was dropped. Check every generated file and fill in missing values by hand.

Checking and troubleshooting

NO_PUBKEY 8D81803C0EBFCD88, “The following signatures couldn’t be verified because the public key is not available”, or “No good signature from required signer”
The Signed-By file is missing or holds a different key than the one that signed the repository. Check it with gpg --show-keys FILE. A 16-digit ID in the error is the last 16 hex digits of the signing key’s fingerprint.
“The key(s) in the keyring … are ignored as the file is not readable…” or “…has an unsupported filetype”
Fix the permissions (sudo chmod 0644 FILE) or the extension: .asc for armored, .gpg for binary. A file holding a GnuPG keybox (made with gpg --keyring … --import) is not accepted; export the key again as shown above.
It worked on Debian 12 and fails on Debian 13
The key was probably only in /etc/apt/trusted.gpg, which APT 3.0 ignores, or the repository uses an algorithm the new policy rejects (see the note above).
“Conflicting values set for option Signed-By regarding source …”
The same repository is listed twice with different keys, often once in a .list and once in a .sources file. Keep one.

Limit what a vendor can replace. A Signed-By key decides which repository is authentic, but packages from that repository can still replace Debian packages. Debian’s third-party guidance also recommends an APT pinning file per repository (for example Pin: origin download.docker.com with Pin-Priority: 100) so that the vendor’s packages do not take priority over the distribution’s own packages (Debian’s default priority is 500).

Sources