RPM (YUM/DNF)
YUM/DNF repository with generated repodata/, optional comps.xml groups and GPG-signed metadata.
| Format id | rpm |
| Repo URL | HOST/repos/REPO (metadata repodata/repomd.xml, packages Packages/) |
| Repository types | hosted, proxy, group (details) |
Client setup
internal/clientsetup produces:
# /etc/yum.repos.d/REPO.repo
[REPO]
name=REPO
baseurl=HOST/repos/REPO
enabled=1
repo_gpgcheck=1
gpgkey=HOST/repos/REPO/public.keycurl -u USERNAME:<TOKEN> -F file=@my-package-1.0.x86_64.rpm HOST/api/v1/repositories/REPO/packagesrepomd.xml.asc and public.key exist only when a signing key applies to the repository (a managed key from the admin API or oa gpg, or GPG_ENABLED=true with GPG_KEY_PATH; see Signing). Without signing set repo_gpgcheck=0 and drop gpgkey. For private repositories add username= and password=<TOKEN> to the repo file (the username is ignored).
Upload API
POST /api/v1/repositories/REPO/packages with multipart field file. Needs write permission on REPO (RBAC).
- The part's Content-Type must be empty,
application/x-rpmorapplication/octet-stream. An unreadable package is400 invalid rpm package: ...; a non-rpmrepository is400; a proxy repository405. - Maximum 500 MiB; quota is enforced (quotas).
- Header limits: at most 65535 header index entries and 100000 elements per array tag. A package that declares more dependencies (requires, provides, ...) or more index entries is
400 invalid rpm package: .... A longer file list is accepted but truncated: at most 100000 files and 16 MiB of path strings are stored, sofilelists.xmllists a prefix of the package's files (the package itself is stored whole and installs normally). - The same NEVRA, or the same SHA-256 in the repository, is
409 CONFLICT: packages are immutable. The stored name isname-version-release.arch.rpm. DELETE /api/v1/repositories/REPO/packages/<id>deletes one package by id (only if it belongs toREPO).- There is no
/repos/REPO/uploadroute:POSTorPUTthere answers404with a message namingPOST /api/v1/repositories/REPO/packages. PUT /api/v1/repositories/REPO/compsuploads acomps.xml(max 10 MiB, validated as comps XML). It is served atrepodata/comps.xmland listed inrepomd.xmlas agroupentry.
Generated metadata
Served relative to HOST/repos/REPO:
| Path | Notes |
|---|---|
repodata/repomd.xml | index of the files below |
repodata/<checksum>-primary.xml.gz / .xz, -filelists, -other | one gzip and one xz copy of each |
repodata/comps.xml | only after a comps upload |
repodata/repomd.xml.asc, public.key | only with signing enabled |
Packages/<file>.rpm | package download (application/x-rpm, Cache-Control: max-age=3600); honours quarantine |
- A background worker regenerates metadata after changes. Until the first generation
repomd.xmlanswers503withRetry-After: 5andCache-Control: no-store. - At startup the server regenerates repositories flagged as stale.
- The first upload creates the repository's RPM record with base architecture
x86_64.
Signing
Set GPG_ENABLED=true, GPG_KEY_PATH and GPG_PASSPHRASE (signing): the worker then writes repomd.xml.asc and public.key, which repo_gpgcheck=1 and gpgkey= use. repomd.xml.asc is a standard ASCII-armored detached signature (gpg --verify repomd.xml.asc repomd.xml succeeds with the key from public.key).
Signing packages (sign_packages)
repo_gpgcheck=1 only proves the metadata is genuine. dnf and yum default to gpgcheck=1, which also wants a signature inside each .rpm. Turn on Sign uploaded packages in the repository settings, oa repo update NAME --sign-packages or PATCH /api/v1/repository/NAME with {"sign_packages": true} (hosted rpm repositories only, off by default). Every upload then gets an OpenPGP header signature from the key that signs the repository's metadata (repository key, then global key, then the configured key file; see Signing). Clients configure:
gpgcheck=1
repo_gpgcheck=1
gpgkey=HOST/repos/REPO/public.keyHow it behaves:
- The signed file is the stored file. Signing rewrites the RPM signature header, so the result is a new blob with its own SHA-256 and size. Only that blob is kept; the upload is not stored. The package's size, checksum in
primary.xml,repomd.xmland the API all describe the bytes clients download, sodnfchecksums match. The package payload and header are untouched;rpm -Kreports the digests and the signature as OK. - Uploading the original again is a duplicate (
409, same NEVRA). Keep the unsigned file if you need it elsewhere. - No key, no upload. If
sign_packagesis on and no key applies, the upload fails with409 SIGNING_KEY_REQUIRED; nothing is stored unsigned. A package that cannot be signed is422 SIGNING_FAILED: the signer needs the payload digest rpm 4.14+ writes and the rpm 4 package format (packages built with_rpmformat 6are not supported yet). - Existing packages are not re-signed. Turning the setting on affects later uploads; rotating the key does not touch packages already stored, which stay valid against the key that signed them. Keep the old public key in the clients' keyring (
rpm --import) or re-upload the packages after deleting them. - Proxy repositories serve upstream packages with the upstream signature;
sign_packagesis rejected for them.
Debian repositories need nothing equivalent: apt verifies the signed Release/InRelease, which already covers every package through its checksums.
primary.xml lists each package's installed size (RPMTAG_LONGSIZE/SIZE) and archive size (rpm 4 signature tag 271/1007, or PAYLOADSIZEALT for rpm 6 packages), which dnf uses for its transaction size checks. Packages uploaded by earlier versions are re-read from storage the first time their repository's metadata is regenerated, which happens once at the first start after the upgrade: migration 064 flags every hosted RPM repository for regeneration, and the metadata worker processes the flagged ones at startup. No new upload is needed.
Proxy
An RPM proxy repository mirrors repodata/ and Packages/ from upstream_url. .rpm files below a Packages/ directory are immutable once cached; .rpm files elsewhere (flat layouts) and repodata/ are served from cache until cache_ttl_seconds (default 1 hour) and then revalidated. Upstream metadata and signatures pass through unchanged, so clients must trust the upstream's key. Negative caching and serve-stale: hosted, proxy, group.
Group
An RPM group publishes merged repodata/ (repomd.xml, primary, filelists, other and modules), signed with the server key (repomd.xml.asc), and serves Packages/ files from the member that owns them. Use baseurl=HOST/repos/GROUP and gpgkey=HOST/repos/GROUP/public.key as for a hosted repository. Merging rules, regeneration and limits: Deb and RPM groups.
A proxy member whose upstream is a tree of repositories (no repodata/ at the root, one per sub-directory such as 9/BaseOS/x86_64/os/) cannot be merged into the group's index. The group serves such a member's sub-paths unmerged: use baseurl=HOST/repos/GROUP/9/BaseOS/x86_64/os/, one repository per baseurl. If the group has nothing else to list, HOST/repos/GROUP/repodata/repomd.xml answers 404 with a message that says so. Details: Deb and RPM groups.