How to set up a
private RPM repository
A private RPM repository gives you full control over which packages reach your AlmaLinux, Rocky Linux, RHEL, Fedora, or openSUSE servers — with GPG verification, CVE screening, and a complete audit trail.
Why teams move away from public mirrors
Public RPM mirrors (dnf install,
yum install)
are convenient but create invisible risk for production infrastructure:
- Uncontrolled updates. A dnf update on Monday can install a different version than the same command run on Friday. There is no snapshot isolation.
- No vulnerability gate. Packages with active CVEs are served identically to clean ones. The EPSS score and CISA KEV status of what you are installing is unknown at install time.
- Supply chain exposure. A compromised upstream maintainer account can push a malicious update to every server in your fleet via a standard dnf upgrade.
- No compliance evidence. NIS2 Article 21 requires documented evidence of your software supply chain controls. A raw public mirror provides none.
How RepoD handles RPM repositories
RepoD uses createrepo_c to manage RPM metadata and
applies a GPG detached signature on repomd.xml
— the standard way for dnf clients to verify repository integrity.
Every .rpm file
is validated with rpm -qip
before acceptance, then scanned by ClamAV and Grype before the package is indexed.
The CVE enrichment pipeline uses Grype's RPM-aware PURL identifiers
(pkg:rpm/) and
maps vulnerabilities against the correct distro CPE — so
almalinux:8 CVEs
are not confused with fedora:42 ones.
Supported distributions
almalinux8 almalinux9 rocky8 rocky9 centos-stream9 oraclelinux8 fedora opensuse-leap-15.6 opensuse-tumbleweed Quick start
git clone https://github.com/getautoflow/repod.git cd repod cp .env.example .env && cp backend.env.example backend.env # Set JWT_SECRET_KEY and ADMIN_PASSWORD_HASH in backend.env docker compose up -d
# Get a token
TOKEN=$(curl -s -X POST http://localhost:8100/auth/token \
-H 'Content-Type: application/json' \
-d '{"username":"YOUR_ADMIN_USERNAME","password":"YOUR_ADMIN_PASSWORD"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# Upload
curl -X POST http://localhost:8100/upload/ \
-H "Authorization: Bearer $TOKEN" \
-F "file=@./nginx-1.27.3-1.x86_64.rpm" \
-F "distribution=almalinux9" # /etc/yum.repos.d/repod.repo [repod] name=RepoD Private Repository baseurl=http://your-repod-host/rpm/almalinux9/ enabled=1 gpgcheck=1 gpgkey=http://your-repod-host/rpm/almalinux9/repodata/repomd.xml.asc
sudo dnf makecache sudo dnf install nginx
CVE scanning for RPM packages
Grype uses distribution-specific CPE mappings to match CVEs accurately for each RPM distro. EPSS scores are enriched from FIRST.org to indicate exploit probability, and the CISA KEV catalogue flags vulnerabilities that are actively exploited in the wild — giving your security team actionable priority signals beyond raw CVSS scores.
Air-gap operation
After initial setup, RepoD runs fully offline. CVE databases and EPSS scores are cached locally. For true air-gap environments, you can pre-populate the CVE database from a network-connected host and rsync it to your isolated RepoD instance — no direct internet access required at scan time.
This makes RepoD suitable for classified or regulated environments where outbound network connections are prohibited.