Recommended resources
The freely available resources this lesson will direct you to.
Hello. In the previous lesson, you separated APT’s repository view from dpkg’s local installation database, and used apt-cache policy to distinguish an installed version from APT’s candidate version. Now you will change the repository view itself—carefully.
The operational goal is not merely “make APT install software from somewhere else.” A safe third-party repository setup must answer two independent questions:
- Trust: Which signing key is allowed to authenticate this repository’s metadata?
- Selection: If this repository and Ubuntu both offer a package, which version should APT choose?
By the end, you should be able to create a modern Ubuntu 24.04 repository definition using a dedicated keyring and Signed-By, then inspect and control priority with apt-cache policy and APT preferences.
Trust and priority are separate controls
An APT repository publishes signed metadata, normally an InRelease file or a Release file plus a signature. That metadata describes package indexes and their checksums. When APT runs apt update, it verifies the metadata signature before accepting the package lists.
A signing key therefore answers:
“May this key authenticate metadata for this source?”
It does not answer:
“Should packages from this source outrank Ubuntu packages?”
That is a priority, or pinning, decision.
Keeping these controls separate prevents a common mistake: importing a repository key globally and assuming that makes the repository safe to use. A globally trusted key can potentially authenticate metadata for any configured source. By contrast, a Signed-By setting ties a source entry to the particular keyring file that APT may use for that source.
A secure repository configuration has four parts:
| Component | Purpose |
|---|---|
| Vendor repository URL | Where APT retrieves metadata and packages |
| Distribution suite and component | Which repository collection APT should use |
| Dedicated keyring file | Stores the vendor’s public signing key |
Signed-By field |
Restricts that source to the designated keyring |
Do not use apt-key add. It places keys into legacy global trust locations and is deprecated. Likewise, do not “fix” a signature failure using trusted=yes, insecure-repository options, or by disabling verification. Those changes remove the protection you are trying to configure.
Before trusting a key, obtain it from the repository operator and compare its full fingerprint with a fingerprint published through an independent vendor-controlled channel. HTTPS protects a download channel, but the fingerprint comparison is what helps establish that you obtained the intended signing key.
Third party repository usage - Ubuntu Server documentation
Read the Ubuntu Server documentation for the supported approach to third-party APT repositories. It establishes the key practices for this lesson: a separate keyring, a source-specific Signed-By reference, repository files in sources.list.d, and pinning.
In “Add the repository”, begin with “Fetch the GPG key.” Read key formats and the remainder of that subsection. Focus on why an ASCII-armored key may be converted to a binary keyring and why apt-key is no longer recommended. Next, in “Sources file entry,” read the full subsection. Locate the explanation beginning with the source file location. Note the distinction between the current deb822 .sources format and the older one-line .list format. Finally, read “Configure pinning for the repository.” The recommendation beginning with pinning is especially relevant. Treat the example priority values as policy choices to verify, not values to copy blindly.
Building a scoped repository definition
On Ubuntu 24.04, use a deb822 source file ending in .sources. Place a third-party source in its own file under:
/etc/apt/sources.list.d/
Store its dedicated keyring under:
/usr/share/keyrings/
This lesson uses /usr/share/keyrings/ because it is the location used in the Ubuntu documentation. A keyring that is locally managed rather than supplied by a package is also commonly stored under /etc/apt/keyrings/; the essential requirement is that the source’s Signed-By field names the exact keyring file.
1. Obtain and inspect the signing key
The following is a template, not a command to run unchanged. Replace the URL, filename, and expected fingerprint with values supplied by the actual repository operator.
curl -fsSLo /tmp/acme-archive-keyring.asc \
https://packages.example.invalid/keys/acme-archive-keyring.asc
gpg --show-keys --with-fingerprint /tmp/acme-archive-keyring.asc
Inspect the displayed fingerprint. Compare it character-for-character against the fingerprint published by the vendor. Do not accept a key merely because its user ID looks plausible.
If it matches, convert the ASCII-armored key to a binary keyring and make it readable by APT:
sudo gpg --dearmor --yes \
--output /usr/share/keyrings/acme-archive-keyring.gpg \
/tmp/acme-archive-keyring.asc
sudo chmod 0644 /usr/share/keyrings/acme-archive-keyring.gpg
rm /tmp/acme-archive-keyring.asc
The binary .gpg form works with both modern deb822 sources and older one-line source files. Ubuntu 24.04 can also use an armored .asc key directly in a deb822 source, but dearmoring gives you a format that remains compatible with either source syntax.
A missing key, an incorrect key, or a keyring unreadable by the APT sandbox user will cause signature verification to fail during apt update.
2. Create the deb822 source file
Create one source file for one repository. This makes review, troubleshooting, and removal straightforward.
sudo tee /etc/apt/sources.list.d/acme.sources >/dev/null <<'EOF'
Types: deb
URIs: https://packages.example.invalid/apt
Suites: noble
Components: main
Architectures: amd64
Signed-By: /usr/share/keyrings/acme-archive-keyring.gpg
EOF
Every uppercase or example value must be replaced with values from the repository provider’s documentation. In particular:
URIsmust be the vendor’s documented APT repository endpoint.Suitesmust be a suite the vendor actually publishes. It might benoble,stable, or another documented value.Componentsis oftenmain, but should not be guessed.Architecturesshould include only architectures you intend to use and that the repository supports.Signed-Bymust be an absolute path to the specific keyring file you verified.
Do not point a Debian repository at Ubuntu merely because the package name looks useful. Do not substitute a different Ubuntu release suite because it “almost matches.” Repository metadata, dependencies, and ABI expectations are tied to the distribution and release.
The earlier one-line syntax remains useful to recognize during troubleshooting:
deb [arch=amd64 signed-by=/usr/share/keyrings/acme-archive-keyring.gpg] https://packages.example.invalid/apt noble main
For a new Ubuntu 24.04 configuration, prefer the more readable deb822 form above.
3. Refresh indexes and verify authentication
Run:
sudo apt update
A successful update should retrieve repository metadata without errors such as:
NO_PUBKEY
The following signatures couldn't be verified
The repository ... is not signed
A successful apt update proves that APT can retrieve and authenticate the repository metadata using the configured source and keyring. It does not prove that the repository should be allowed to replace Ubuntu packages. That is the next control.
You can also verify that the intended source is present and that there is not a duplicate definition elsewhere:
grep -R --line-number --fixed-strings 'packages.example.invalid' \
/etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null
A repository accidentally defined twice can produce duplicate-target warnings or conflicting Signed-By settings. Keep exactly one active definition for a given repository collection.
How APT chooses a candidate version
APT assigns a priority to each known package version. It normally selects the version with the highest priority; if versions have equal priority, it normally selects the higher version number.
Typical default priorities include:
| Situation | Typical priority |
|---|---|
| Version already installed locally | 100 |
| Version from an ordinary configured repository | 500 |
| Version from the configured target release | 990 |
| Repository marked as not automatic | 1 or 100 |
These are typical values, not universal constants. A repository can publish metadata that changes defaults, and local preference rules can override them.
The important consequence is this: if Ubuntu and a third-party repository both have default priority , and the third-party repository supplies a higher version, APT may select the third-party version as the candidate. That can be intentional for a dedicated application, but it is a poor default for packages that overlap with core Ubuntu components.
APT normally avoids downgrades even if a lower version has a higher priority. A pin priority above can override that safeguard, which is why such priorities should be used only for a deliberate recovery or rollback plan.
Read the Ubuntu apt-cache manual page’s description of policy. This is the command you will use to verify both overall source priorities and the candidate-selection result for a particular package.
In the “policy” entry near the end of the page, read the policy description. Retain the two modes: apt-cache policy without arguments lists source priorities, while apt-cache policy PACKAGE explains selection for one package.
Start by inspecting the unmodified state:
apt-cache policy
Then inspect a package available from the third-party repository:
apt-cache policy VENDOR_PACKAGE
A representative result might look like this:
vendor-agent:
Installed: (none)
Candidate: 3.2.1-1
Version table:
3.2.1-1 500
500 https://packages.example.invalid/apt noble/main amd64 Packages
If a package is available from both Ubuntu and the external repository, the output may show several versions. Your key questions are:
- Which source provides each version?
- What priority does each version have?
- Which version is marked as Candidate?
- Is that candidate consistent with the intended policy?
Pinning a repository without weakening its trust boundary
Pinning changes selection. It does not grant trust and it does not replace Signed-By.
A practical policy for a general third-party repository is often:
- Permit packages unique to that repository.
- Permit updates to packages deliberately installed from it.
- Prevent it from automatically replacing packages that Ubuntu also provides.
A priority of often supports that policy. Create a preference file:
sudo tee /etc/apt/preferences.d/acme >/dev/null <<'EOF'
Package: *
Pin: origin "packages.example.invalid"
Pin-Priority: 100
EOF
Replace packages.example.invalid with the repository host name from the actual source URL. Do not include https:// or a path.
This rule means:
- It applies to all packages from that host.
- Those versions receive priority .
- An Ubuntu version at priority remains preferred.
- A package available only from the third-party repository can still be explicitly installed.
- Subsequent newer versions from that repository can remain candidates for an installed package when no higher-priority source competes.
There is an important naming distinction:
Pin: origin "host.example"matches the hostname from the repository URI.Pin: release o=VendorNamematches the repository metadata’s Origin field.
Use the hostname form when it clearly identifies the source you intend to constrain. If you use a release-based pin, first inspect APT’s output and copy the actual release attributes rather than guessing the vendor’s labels.
After adding or changing a preferences file, re-run policy inspection:
apt-cache policy
apt-cache policy VENDOR_PACKAGE
Preferences take effect immediately; they do not require a new download. Running apt update is still appropriate when you have changed the source file or keyring.
For a package offered by both Ubuntu and the repository, you might now see:
example-tool:
Installed: 2.0-0ubuntu1
Candidate: 2.0-0ubuntu1
Version table:
3.0-1 100
100 https://packages.example.invalid/apt noble/main amd64 Packages
*** 2.0-0ubuntu1 500
500 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages
Although is newer, the Ubuntu version remains the candidate because its priority is higher. This is precisely why checking Candidate matters more than simply noticing the highest version number.
Before installing a package from the external repository, simulate the transaction:
apt-get -s install VENDOR_PACKAGE
The -s option simulates package actions without changing the system. Read the proposed package list and confirm that only expected packages would be installed, upgraded, or removed.
A useful final configuration check is:
sudo apt update
apt-cache policy VENDOR_PACKAGE
apt-get -s install VENDOR_PACKAGE
This sequence verifies, respectively:
- Metadata can be downloaded and authenticated.
- APT sees the package and selects the intended candidate.
- The proposed installation transaction matches the policy.
Troubleshooting repository and priority failures
Repository failures are easier to solve when you distinguish trust, source syntax, and selection.
Signature or key errors
If apt update reports NO_PUBKEY or a signature verification failure, check:
ls -l /usr/share/keyrings/acme-archive-keyring.gpg
gpg --show-keys --with-fingerprint \
/usr/share/keyrings/acme-archive-keyring.gpg
grep -R --line-number --fixed-strings 'Signed-By' \
/etc/apt/sources.list.d 2>/dev/null
Confirm all of the following:
- The
Signed-Bypath is absolute and spelled exactly as the keyring path. - The keyring contains the expected fingerprint.
- The keyring is readable.
- The repository is currently signed by that key.
- There is no duplicate source definition pointing to the same repository with a different trust setting.
Do not respond by placing the key in trusted.gpg, running apt-key add, or removing signature checks.
“Does not have a Release file”
This usually means the URL, suite, or component is wrong, or the vendor does not publish packages for your requested Ubuntu release. Return to the vendor’s repository documentation and correct the source fields. Do not use an unsupported suite just to make the error disappear.
The wrong package is the candidate
Inspect the exact selection:
apt-cache policy PACKAGE
Then inspect all source priorities:
apt-cache policy
Common causes include:
- no pinning rule exists;
- the pin matches the wrong hostname;
- a second source definition uses another host;
- the package is covered by a more specific preference rule;
- both sources retain priority , allowing the higher version to win.
Remember that the package-specific apt-cache policy PACKAGE output is the definitive evidence for candidate selection.
Removing a repository cleanly
To stop using a repository, remove its source and its preference file:
sudo rm /etc/apt/sources.list.d/acme.sources
sudo rm /etc/apt/preferences.d/acme
sudo apt update
Remove the keyring only if no remaining source uses it:
sudo rm /usr/share/keyrings/acme-archive-keyring.gpg
Removing a repository does not remove packages that were already installed from it. Those packages remain installed until you explicitly replace, remove, or purge them in a later package-management operation.
You now have a controlled model for third-party APT repositories: verify the signing key’s fingerprint, store it in a dedicated keyring, scope it with Signed-By, and verify the resulting candidate version with apt-cache policy. Trust establishes what metadata APT may accept; pinning establishes what packages APT should prefer.
Next, you will use this foundation to install, upgrade, hold, remove, and purge packages while predicting dependency and configuration-file effects.