Skip to content

Distribution Management

A conceptual guide to how Repod models distributions, why the distinction matters, and how packages flow between them.


What is a distribution?

A distribution in Repod represents a target operating system release. When you upload or import a package, you assign it to a specific distribution. Only clients configured for that distribution will see the package.

The concept maps directly to the native package manager notion of a target:

An APT distribution is a codename (jammy, noble, bookworm). It appears in the sources.list entry:

deb http://repo.example.com/repos jammy main

Behind the scenes, reprepro manages a directory tree under /repos/dists/jammy/ containing InRelease, Release, and the Packages index.

An RPM distribution is a path component (almalinux9, fedora, rocky9). It appears in the .repo file:

baseurl=http://repo.example.com/repos/almalinux9/x86_64/

Behind the scenes, createrepo_c manages a directory tree under /repos/almalinux9/x86_64/ containing repodata/repomd.xml and the RPM files.


Why distributions matter

Binary compatibility

A .deb built for Ubuntu 22.04 (jammy) may not install correctly on Ubuntu 24.04 (noble) β€” different glibc version, different default Python, different SSL library ABI. Keeping distributions separate prevents mismatched packages from reaching the wrong OS.

CVE accuracy

Grype uses the distribution ID (almalinux:9, rockylinux:9, etc.) to filter CVE advisories. A vulnerability may be patched in AlmaLinux 9 but not in AlmaLinux 8. Without the correct distribution context, Grype would return false positives or miss relevant advisories.

The Ubuntu security team backports fixes, so the CVSS score alone does not determine exploitability. Grype correlates against Ubuntu Security Notices (USN) per-release, which requires the codename to be known.

Rollout control

Distributions serve as staging tiers. A common pattern:

Development β†’ QA β†’ Production
(focal)       (jammy)   (noble)

Package promotion moves a binary between distributions without re-uploading or re-scanning (the CVE decision is preserved). See Promoting packages.


Supported distributions

Codename OS Architecture
jammy Ubuntu 22.04 LTS amd64
noble Ubuntu 24.04 LTS amd64
focal Ubuntu 20.04 LTS amd64
bookworm Debian 12 amd64
Codename OS Architecture Grype distro ID
almalinux8 AlmaLinux 8 x86_64 almalinux:8
almalinux9 AlmaLinux 9 x86_64 almalinux:9
rocky8 Rocky Linux 8 x86_64 rockylinux:8
rocky9 Rocky Linux 9 x86_64 rockylinux:9
centos-stream9 CentOS Stream 9 x86_64 centos:9
fedora Fedora 42 x86_64 fedora:42
opensuse-leap-15.6 openSUSE Leap 15.6 x86_64 opensuse.leap:15.6

Repository filesystem layout

Understanding the on-disk layout helps when troubleshooting or integrating with other tools.

/repos/
β”œβ”€β”€ conf/
β”‚   └── distributions           ← reprepro configuration
β”œβ”€β”€ db/                         ← reprepro internal database
β”œβ”€β”€ dists/
β”‚   β”œβ”€β”€ jammy/
β”‚   β”‚   β”œβ”€β”€ InRelease           ← GPG-signed index
β”‚   β”‚   β”œβ”€β”€ Release
β”‚   β”‚   β”œβ”€β”€ Release.gpg
β”‚   β”‚   └── main/
β”‚   β”‚       β”œβ”€β”€ binary-amd64/
β”‚   β”‚       β”‚   β”œβ”€β”€ Packages
β”‚   β”‚       β”‚   β”œβ”€β”€ Packages.gz
β”‚   β”‚       β”‚   └── Packages.xz
β”‚   β”‚       └── Contents-amd64.gz
β”‚   β”œβ”€β”€ noble/
β”‚   └── bookworm/
└── pool/
    └── main/
        └── n/nginx/
            └── nginx_1.24.0-1_amd64.deb

The pool/ directory is shared across distributions. A package binary is stored once; reprepro's includedeb command creates the index entry that makes it visible in a specific distribution.

/repos/
β”œβ”€β”€ almalinux9/
β”‚   └── x86_64/
β”‚       β”œβ”€β”€ repodata/
β”‚       β”‚   β”œβ”€β”€ repomd.xml          ← main index
β”‚       β”‚   β”œβ”€β”€ repomd.xml.asc      ← GPG detached signature
β”‚       β”‚   β”œβ”€β”€ primary.xml.gz      ← package metadata
β”‚       β”‚   β”œβ”€β”€ filelists.xml.gz
β”‚       β”‚   └── other.xml.gz
β”‚       └── nginx-1.24.0-1.el9.ngx.x86_64.rpm
β”œβ”€β”€ rocky9/
β”‚   └── x86_64/
└── fedora/
    └── x86_64/

Each RPM distribution has its own complete directory. Unlike APT, RPM packages are not deduplicated across distributions β€” each distribution holds its own copy of the binary.


Promoting packages

Promotion moves a package from one distribution to another without re-uploading or re-scanning. The CVE decision record and justification are preserved.

# Promote nginx from jammy (dev/QA) to noble (production)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/api/v1/distributions/promote \
  -d '{"package":"nginx","from_dist":"jammy","to_dist":"noble"}'

What happens internally:

  1. Repod copies the .deb binary in pool/ (or creates a hard link).
  2. reprepro includedeb <to_dist> <path> adds the package to the target distribution's index.
  3. reprepro re-signs the InRelease file for the target distribution.
  1. Repod copies the .rpm file to the target distribution directory.
  2. createrepo_c --update re-generates the repodata/ index.
  3. The repomd.xml.asc GPG signature is regenerated.

Role required

Promotion requires the maintainer or admin role.


Migrating packages between distributions

Migration copies all packages from one distribution to another. Useful when upgrading your OS baseline across your infrastructure.

# Migrate all packages from focal to jammy
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/api/v1/distributions/migrate \
  -d '{"from_dist":"focal","to_dist":"jammy"}'

Source distribution is not removed

After migration, packages exist in both distributions. Remove packages from the source distribution manually if needed.


Initializing distributions

Distributions are initialized automatically on first startup. If you need to re-initialize (e.g., after restoring a backup without the distribution tree):

curl -X POST -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/api/v1/distributions/init

Adding unsupported distributions

The distribution list is defined in the backend source code (services/distributions.py). Adding a new distribution requires:

  1. Editing services/distributions.py to add the new codename / path.
  2. Rebuilding the backend image: docker compose build backend-api.
  3. Calling POST /api/v1/distributions/init to create the on-disk structure.

For APT, you also need to add the new codename to conf/distributions.

This is intentionally gated behind a code change to prevent accidental distribution sprawl in production.


Multi-architecture support

The current release supports:

Edition Architecture
APT amd64
RPM x86_64

ARM (arm64 / aarch64) support is planned for a future release. The upload endpoint currently rejects packages with non-supported architectures.