Skip to content
Built-in catalog

Built-in catalog

Status: partly implemented. The built-in distros today are ubuntu-22.04, ubuntu-24.04, debian-12 and debian-13: packtrix catalog list shows them. The other distros and the vendor repositories below are planned.

The catalog is Packtrix’s built-in knowledge of repositories: URLs, components, architectures and pinned signing keys. It lets a manifest say distro: rocky-9 instead of listing URLs and key files, and lets Packtrix find vendor repositories on its own.

Built-in distros

Used with targets[].distro:

FamilyNamesNotes
Ubuntuubuntu-22.04, ubuntu-24.04, ubuntu-26.04amd64 comes from archive.ubuntu.com and security.ubuntu.com; arm64 from ports.ubuntu.com. The catalog handles the difference.
Debiandebian-12, debian-13
Rocky Linuxrocky-8, rocky-9, rocky-10
AlmaLinuxalmalinux-8, almalinux-9, almalinux-10
CentOS Streamcentos-stream-9, centos-stream-10CentOS Linux 7 and 8 are end-of-life and not included
RHELrhel-8, rhel-9, rhel-10Requires distro.auth with your subscription certificates

Every distro entry defines its official repositories (for Ubuntu: release, -updates, -security), default components and signing keys.

Components

distro.components selects parts of the distribution. It is the same field for every family:

UbuntuDebianRocky / Alma / RHEL / CentOS Stream
Defaultmain, universemainbaseos, appstream
Optionalrestricted, multiversecontrib, non-free, non-free-firmwarecrb, extras, highavailability

The catalog maps these to vendor-specific names. For example, crb becomes powertools on EL8 and codeready-builder-… on RHEL. Ubuntu defaults to main plus universe because many common server packages, such as glusterfs-client on 24.04, live in universe.

Built-in vendor repositories

Added automatically when a listed package needs them (see auto-detection):

NameProvidesdebrpmNotes
dockerdocker-ce, containerd.io, plugins✅✅
kubernetes@<minor>kubelet, kubeadm, kubectl, cri-tools, kubernetes-cni✅✅One repository per Kubernetes minor version; chosen by the package version
cri-o@<minor>cri-o✅✅One repository per minor version
nvidia-container-toolkitnvidia-container-toolkit and its libraries✅✅
epel-<major>Extra packages for Enterprise Linux—✅Community repository. Turns on crb automatically, which EPEL requires.

Later, based on demand: helm, hashicorp, postgresql, grafana, nginx-org.

Container tools such as podman, buildah and skopeo are already in the distro repositories and need no vendor repository.

Auto-detection

You list packages, not vendor repositories. For each target, Packtrix decides where each package comes from:

for each package in the manifest:
  1. available in the distro (or in your extraRepos)?   → use it; the distro always wins
  2. otherwise, look it up in the catalog for this distro and architecture:
       no repository provides it        → error, with "did you mean" suggestions
       exactly one repository           → add that repository
       a versioned family (kubernetes)  → choose by the package's `version`; no version → error
       several unrelated repositories   → error: ambiguous, set `repo:`
then add any repository the chosen ones require (epel → crb) and resolve dependencies.

Rules that keep this predictable:

RuleWhy
The distro always winsnginx exists in Ubuntu and on nginx.org. Silently swapping in a third-party build would change what you run. Opt in per package with repo: nginx-org.
Versioned repositories need a versionPicking a Kubernetes minor version silently could break version-skew rules. version: "1.34.*" selects kubernetes@1.34.
Only top-level packages trigger detectionDependencies must resolve from the repositories already chosen. Vendor packages depend on their own repository or the distro, so this covers real cases.
Everything is visibleplan lists each repository that was added automatically and which package caused it, and flags new signing keys. The lockfile records the source of every package.
Organizations can refuseautoRepos.deny: [epel-*] blocks a repository. autoRepos.enabled: false turns detection off completely.
Only built-in distrosTargets defined from scratch with repos have no catalog mapping, so detection is off for them.

Where the data comes from

  • Each catalog release ships a name index: which package names each vendor repository provides, for each distro and architecture.
  • At plan time, Packtrix also reads the live index of each candidate repository, so packages that a vendor added after the catalog release are still found.

Catalog entry format

The catalog lives in internal/catalog/ and is embedded in the binary: catalog.yaml holds its version, distros/<name>.yaml one distro each, and keys/<name>.asc the keys. A distro entry looks like this:

# internal/catalog/distros/ubuntu-24.04.yaml (excerpt)
name: ubuntu-24.04
family: ubuntu                      # ubuntu, debian or el
format: apt
release: { version: "24.04", codename: noble }
architectures: [amd64, arm64]
components:
  - { name: main, default: true }
  - { name: universe, default: true }
  - { name: restricted }
repositories:
  - name: security
    url: https://security.ubuntu.com/ubuntu/
    archURLs:                       # where an architecture lives elsewhere
      arm64: https://ports.ubuntu.com/ubuntu-ports/
    suite: noble-security
    keys: [ubuntu-archive-2018]     # → keys/ubuntu-archive-2018.asc
baseline: { repository: release, component: main }
keys:
  ubuntu-archive-2018: F6EC B376 2474 EDA9 D21B  7022 8719 20D1 991B C93C

RPM entries have no suite. Their URLs contain {component} and {arch}, and a component’s path gives its name in the URL, such as PowerTools for crb on EL8. The baseline names where minimal baselines come from.

When Packtrix loads the catalog, it checks every entry, and that every key file contains the key its fingerprint pins. make test-live also fetches the InRelease file of every repository, for every architecture, and verifies it with the pinned keys.

Vendor repositories will use the same fields as custom repositories, pre-filled:

# catalog/vendors/kubernetes.yaml
name: kubernetes
params:
  version: { required: true, pattern: '^1\.\d+$' }
deb:
  url: "https://pkgs.k8s.io/core:/stable:/v{version}/deb/"
  suite: "/"
rpm:
  url: "https://pkgs.k8s.io/core:/stable:/v{version}/rpm/"
keys: [kubernetes-release]          # → catalog/keys/, pinned by fingerprint

Vendor URL layouts are irregular, and absorbing that is the catalog’s job. For example, Docker uses its centos path for Rocky and AlmaLinux, but its rhel path for RHEL.

Inclusion policy

A repository is added to the catalog only if:

  • it is the official repository of the vendor or project, not a mirror, PPA or personal repository;
  • it is signed, and its key fingerprint is published in the vendor’s own documentation;
  • it is used widely enough to justify maintenance.

Maintenance

Repositories move and keys rotate. For example, Kubernetes moved from apt.kubernetes.io to pkgs.k8s.io in 2023, and the old repositories were later shut down. The catalog is maintained like this:

  • Nightly checks in CI: for every entry, fetch the index, verify its signature with the pinned key, and resolve a test package. Broken entries are found before users hit them.
  • Separate release cycle: the catalog is versioned independently of the binary. packtrix catalog update fetches a newer signed catalog, and a snapshot is embedded in each binary so it works offline.
  • Pinned in the lockfile: the catalog version is recorded, so updating the catalog never changes an existing lockfile silently.
  • Organization catalogs (planned): --catalog <path or URL> adds your own entries next to the built-in ones.