Your own RPM mirror, and building the fix before the distro ships it
How to run a consistent local RPM mirror and, when a fix is out upstream but not yet packaged, build it cleanly so the official package takes over later.

When a serious vulnerability is published, two things decide how long your servers stay exposed: how fast you can fetch packages, and whether the fixed package exists yet. A local mirror solves the first. The ability to build a clean package yourself, from the maintainer's own specification, solves the second. This is how we do both on Enterprise Linux, and how to do it without leaving home-made packages lying around after the official ones arrive.
Why run your own mirror at all
Public mirrors are run by generous people and organisations, and on an ordinary day they are fast. On the day a critical fix lands, every server in the world asks for the same packages at once. A local mirror inside your own network turns that into a local copy operation. It also lets you patch servers that are deliberately cut off from the internet, such as backup and management systems, without opening them up.
There is a quieter benefit: a mirror that keeps full package history lets you rebuild a server to a known state, or roll a package back, from your own copy.
Mirror without ever serving a half-synced repository
The naive mirror is an rsync or reposync straight into the directory your web server publishes. If a client runs dnf while the sync is halfway through, it can see new metadata that refers to packages that have not arrived yet, or old metadata pointing at packages that were just pruned. The result is confusing errors at exactly the wrong moment.
Sync in two phases instead:
- Packages first. Fetch the packages into the published directory. Old metadata still describes a consistent set, because nothing it refers to has been removed yet.
dnf reposync --repoid=os-baseos --arch=x86_64 --arch=noarch \
--delete --norepopath -p /srv/repo/os/baseos
- Metadata second, swapped at once. Download the upstream
repodata/into a sibling directory (every file thatrepomd.xmlreferences), verify it, then replace the published metadata with a single rename. Clients see the old repository or the new one, never a mixture.
mv /srv/repo/os/baseos/repodata /srv/repo/os/baseos/repodata.old
mv /srv/repo/os/baseos/repodata.new /srv/repo/os/baseos/repodata
Using the upstream metadata, rather than regenerating your own with createrepo_c, keeps the repository exactly as the publisher built it.
Give each sync job a lock so two runs never overlap, write a small status file with the result and the time, and alert someone when a job fails. A mirror that silently stopped syncing last month is worse than no mirror, because everyone trusts it.
Clients then use a plain repository file pointing at your mirror:
[local-baseos]
name=Local mirror: BaseOS
baseurl=https://mirror.example.internal/os/baseos/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
When the fix exists but the package does not
Sometimes the upstream project ships a fixed release and the distribution or repository maintainer has not packaged it yet. Often that gap is hours; sometimes it is days.
The rule we follow: build the upstream release from the maintainer's own packaging, change nothing else, and make sure the official package replaces yours automatically.
Build in a clean room
Use a fresh container of the same distribution release as the target servers, so the build environment is known and disposable:
podman run --rm -it -v ./build:/build:Z oraclelinux:<release> bash
dnf install -y rpm-build rpmdevtools dnf-plugins-core
rpmdev-setuptree
Start from the maintainer's latest source package (SRPM) for the branch you run, install it, and install its build dependencies:
rpm -ivh php-<current>.src.rpm
dnf builddep -y ~/rpmbuild/SPECS/php.spec
Change only the version, and verify the source
Download the fixed upstream tarball from the project's official site and verify it against the checksum the project publishes.
sha256sum php-<fixed>.tar.xz
In the spec file, update Version to the fixed release and replace the source. Leave patches, configure options and file lists as the maintainer wrote them. If a maintainer patch no longer applies to the new release, stop and understand why before you go further.
Choose a release that steps aside
This is the step people forget. Your package must sort lower than the maintainer's eventual official package of the same version, so that a normal dnf upgrade replaces yours. A release that starts with 0. does this reliably:
Release: 0.1.local%{?dist}
Check it before you build anything, using the same comparison rpm uses:
rpmdev-vercmp 0:X.Y.Z-0.1.local.elN 0:X.Y.Z-1.elN.maint
The official release should come out as newer. If it does not, change your release string until it does.
Build, test, keep the evidence
rpmbuild -ba ~/rpmbuild/SPECS/php.spec
Store the built packages with their checksums and a note of the source tarball's checksum, so anyone can later say exactly what was installed and where it came from.
Publish your builds in a separate repository on your mirror, not mixed into the copy of the vendor's repository. When the official package lands in the mirrored repository, the next normal update replaces yours, and your separate repository can be emptied.
Credit where it is due
None of this replaces the people who maintain the packages. Repository maintainers such as Remi Collet, whose PHP repositories much of the Enterprise Linux world relies on, often ship fixes remarkably quickly. Building from their specification is a way to cover the last few hours, not a substitute for their work. During the PHP2Shell fixes we wrote about both sides of that: PHP2Shell: patched PHP packages and Respect to Remi.
A checklist for your own platform
- Where do your servers fetch packages from, and what happens if that place is slow?
- Can your isolated servers be patched without opening them to the internet?
- Could a client ever see a half-synced repository on your mirror?
- Does anyone get told when a mirror sync fails?
- If you had to build a package tonight, does your release string let the official one replace it?
If you would like help setting up a mirror or an emergency build process, our engineers are glad to share what works. Talk to an engineer
Published 22 October 2026.
All field notes