TT Lab
Get started
Learn Learning paths Courses

RHEL-Family Administration

createrepo_c and repodata

Continue in TT Lab

In one line

createrepo_c scans an rpm directory and builds repodata/. The repomd.xml inside it is the table of contents that points to the other metadata files.

Why this exists

Merely copying RPM files into a directory does not make it a dnf repository. dnf does not open every package each time; it looks up the versions, dependencies, and file lists in repodata. So if you add packages and do not refresh the metadata, the files on the server and the list the client sees differ. If you know this difference, you can tell cache and permission problems apart from a missed repository creation.

How it works

dnf install -y createrepo_c
createrepo_c /srv/repo/labhub

ls -1 /srv/repo/labhub/repodata/
# repomd.xml
# <checksum>-primary.xml.gz
# <checksum>-filelists.xml.gz
# <checksum>-other.xml.gz
File Contents
repomd.xml The table of contents. The location, checksum, and time of each metadata file
primary Name, version, dependencies, summary
filelists The list of all files a package contains
other changelog

The reason file-based search such as dnf provides '*/nginx.conf' works is filelists.

The checksum prefix before the file names is the result of --unique-md-filenames (the default). It prevents the incident in which an intermediate proxy or CDN returns an old copy. The file name changes with the content, so the cache cannot get confused.

Main options

Option Behavior per the documentation
--update "Reuse existing metadata for rpms that have not changed, judged by file size and mtime"
--workers N Number of workers that read rpms
-g, --groupfile Include a comps group file
-s, --checksum The checksum type of repomd and packages. Default sha256
--compress-type bz2, gz, zck, zstd, xz
-i, --pkglist A file listing the packages to include
--retain-old-md N Number of old repodata to keep
-x, --excludes Exclude patterns

-d/--database (creating sqlite) is deprecated. If you see this option in an old procedure document, you can delete it.

Tying --pkglist to the manifest is a good practice. If you use the import manifest as the pkglist as it is, it structurally prevents the incident where the list and the actual repository disagree.

Updating

After adding packages, always rebuild the metadata.

createrepo_c --update /srv/repo/labhub
dnf clean expire-cache
dnf repolist

Forgetting --update is the number one cause of the most frequent report on air-gapped networks. The files are visible, but dnf does not know.

The repo file and verification

[labhub-local]
name=LabHub local (snapshot 2026-08-20)
baseurl=file:///srv/repo/labhub
enabled=1
gpgcheck=0
metadata_expire=-1

Three checks that it works.

dnf repolist -v
dnf repoclosure --repo=labhub-local
dnf install --assumeno <패키지>

dnf repoclosure lists packages that have dependencies that cannot be resolved inside the repository. It is the best tool to use as a gate before an import.

createrepo_c /srv/bundle/rpms
dnf repoclosure --repofrompath=bundle,/srv/bundle/rpms --repo=bundle

--repofrompath specifies a repository temporarily without creating a .repo file. It is useful in verification scripts.

Cleaning up old packages

dnf repomanage --old --keep 2 /srv/repo/labhub
dnf repomanage --old --keep 2 /srv/repo/labhub | xargs -r rm -v
createrepo_c --update /srv/repo/labhub

How many you keep is the rollback range. If the import interval is once a month and you keep 3, about 3 months of rollback is possible.

What it looks like in the field

A snapshot date in the repository display name. If you write it like name=... (snapshot 2026-08-20), six months later one dnf repolist -v line can pin down the point in time.

What you will do in the next lab

You collect rpms and build a repository with createrepo_c, write a .repo file, enable only that repository and install, and after adding a package, refresh it with --update.