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:
Behind the scenes, reprepro manages a directory tree under /repos/dists/jammy/
containing InRelease, Release, and the Packages index.
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:
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:
- Repod copies the
.debbinary inpool/(or creates a hard link). reprepro includedeb <to_dist> <path>adds the package to the target distribution's index.- reprepro re-signs the
InReleasefile for the target distribution.
- Repod copies the
.rpmfile to the target distribution directory. createrepo_c --updatere-generates therepodata/index.- The
repomd.xml.ascGPG 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):
Adding unsupported distributions¶
The distribution list is defined in the backend source code
(services/distributions.py). Adding a new distribution requires:
- Editing
services/distributions.pyto add the new codename / path. - Rebuilding the backend image:
docker compose build backend-api. - Calling
POST /api/v1/distributions/initto 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.