Air-gapped servers and locked-down networks cannot reach public DNF mirrors. The practical approach is to sync entire repositories once on a connected RHEL-family host, then copy the tree to removable media or serve it over HTTP inside your network.
This guide uses dnf reposync with --download-metadata on RHEL 8, 9, and 10, Rocky Linux, AlmaLinux, CentOS Stream, and Oracle Linux. It mirrors binary repositories—not single RPM downloads. For one package plus dependencies, see download an RPM and all dependencies with DNF.
Validation environment: Rocky Linux 10.2 (Red Quartz). I synced the
extrasrepository withdnf reposync --download-metadata, confirmedrepodata/and package signatures, and listed packages through--repofrompath. RHEL subscription repositories were not tested on this host.
Quick Reference: Repository Sync Workflow
| Task | Command or concept |
|---|---|
| List enabled repositories | dnf repolist |
| List all repositories | dnf repolist --all |
| Install reposync | dnf install dnf-plugins-core |
| Sync one repository | dnf reposync --repoid=<id> --download-metadata --gpgcheck -p <path> |
| Remove packages deleted upstream | Add --delete |
| Verify downloaded metadata | Check <path>/<repo-id>/repodata/ |
| Use repository locally | baseurl=file:///path/to/repo |
| Serve repository over HTTP | Point baseurl at the web-server directory |
Red Hat documents dnf-plugins-core and dnf reposync --download-metadata for disconnected RHEL 10 workflows. The same pattern applies across current RHEL-family releases.
Understand reposync, Metadata, and createrepo_c
Three tools show up in offline-repo discussions. They do different jobs:
dnf reposynccopies RPM packages from an enabled remote repository into a local directory.--download-metadatacopies the upstream repository metadata too—package indexes, groups, and modular data when the source provides it.createrepo_cbuilds fresh metadata from a directory of RPMs you assembled yourself.
When you run a full sync with --download-metadata, the downloaded tree is already consumable by DNF. You normally do not run createrepo_c afterward.
Reach for createrepo_c when you create a custom repo from scratch. Avoid rebuilding metadata on a tree that was synchronized with --download-metadata unless you understand what upstream records you may lose.
| Scenario | Recommended method |
|---|---|
| Exact upstream mirror | reposync --download-metadata |
| Manually collected RPM directory | createrepo_c |
| Upstream mirror with packages removed manually | Avoid when possible; regenerated metadata may lose modules, groups, update information, or other upstream records |
--newest-only mirror |
Run createrepo_c --update, then test dependency, group, and module operations before publishing |
DNF warns that --newest-only can leave metadata pointing at packages you never downloaded. The official reposync documentation recommends createrepo_c --update for that mismatch. An exact upstream mirror preserved with --download-metadata remains the safest choice.
Do not casually run createrepo_c over BaseOS, AppStream, or a product repository that was synchronized with --download-metadata.
Generating new package metadata can replace the upstream metadata set without recreating modules, comps groups, update information, or vendor-specific records. Preserve the original repository unchanged whenever you need a faithful offline mirror.
Plan the Offline Repository Mirror
Decide these items before you start pulling gigabytes:
- Target OS major version (do not mix EL8, EL9, and EL10 packages in one tree)
- Repository IDs to mirror (
baseos,appstream,crb, product add-ons, and so on) - Architecture (
x86_64,aarch64, and usuallynoarchalongside the primary arch) - Whether you need source or debuginfo repositories
- Disk space—BaseOS and AppStream together are often tens of gigabytes per major release
- Local copy on USB/NFS versus HTTP hosting for many clients
- Whether you must keep older package versions when upstream drops them
- How often you will refresh the mirror
| Distribution | Common repository categories |
|---|---|
| RHEL | BaseOS, AppStream, CodeReady Builder, plus product repositories tied to your subscription |
| Rocky Linux | BaseOS, AppStream, CRB, Extras |
| AlmaLinux | BaseOS, AppStream, CRB, Extras |
| CentOS Stream | BaseOS, AppStream, CRB |
| Oracle Linux | BaseOS, AppStream, optional public-yum or ULN channels |
Repository IDs change by release, architecture, subscription, and vendor—always run dnf repolist --all on the connected mirror host instead of copying IDs from a blog post.
Never mix packages from different major releases or CPU architectures in one repository directory.
Install reposync and Repository Tools
Install the plugin that provides dnf reposync, plus createrepo_c for the custom-repo cases described above:
sudo dnf install -y dnf-plugins-core createrepo_cdnf-plugins-core supplies dnf reposync on the standard DNF 4 stack used on RHEL 8 through 10 and Rocky/AlmaLinux today. createrepo_c is for modified trees—not routine metadata-preserving mirrors.
On hosts running DNF5 explicitly, the plugin package may be dnf5-plugins and some option names differ (--destdir instead of -p, for example). This article focuses on dnf reposync as documented for RHEL 8–10.
Confirm the tools are available:
dnf reposync --helpThe help text should list --download-metadata, --delete, --download-path, and related flags.
createrepo_c --versionSample output:
Version: 1.1.2 (Features: LegacyWeakdeps )Find and Enable Repository IDs
Start with the repositories already enabled on your mirror host:
sudo dnf repolistSample output on Rocky Linux 10.2:
repo id repo name
appstream Rocky Linux 10 - AppStream
baseos Rocky Linux 10 - BaseOS
crb Rocky Linux 10 - CRB
epel Extra Packages for Enterprise Linux 10 - x86_64
extras Rocky Linux 10 - ExtrasInclude disabled repositories you plan to mirror:
sudo dnf repolist --allSample output (trimmed):
repo id repo name status
appstream Rocky Linux 10 - AppStream enabled
baseos Rocky Linux 10 - BaseOS enabled
crb Rocky Linux 10 - CRB enabled
extras Rocky Linux 10 - Extras enabled
highavailability Rocky Linux 10 - High Availability disabledUse the repo id column as --repoid in reposync commands.
RHEL repositories
The connected mirror host must be entitled to the content you synchronize. Confirm registration and repository access, enable only the repositories you need, and sync from that host.
Many current RHEL environments use Simple Content Access—repository access is managed without manually attaching a pool ID on every host. Avoid treating subscription-manager attach --pool=<pool-id> as the universal first step.
Do not publish subscription-protected RHEL repository content on a public HTTP server.
Rocky Linux, AlmaLinux, and CentOS Stream
Standard .repo files ship with the distribution. Run dnf repolist --all, enable what you need (crb, extras, and so on with dnf config-manager --set-enabled when required), then sync using the repo id values you see locally.
Oracle Linux
Distinguish public Oracle Linux yum repositories from ULN channels that require authorized ULN access. Oracle documents dnf reposync --delete --download-metadata for local mirrors and also offers rsync for larger public repository trees. Mirror public-yum content where possible and use ULN mainly for packages not available in public repositories.
Download an Entire Repository with dnf reposync
Set variables so the example is easy to reuse. On Rocky Linux 10.2 during testing, the small extras repository used extras as its repo ID:
REPO_ID=extras
DEST=/srv/reposRun the sync:
sudo dnf reposync \
--repoid="$REPO_ID" \
--download-metadata \
--gpgcheck \
--download-path="$DEST"--gpgcheck respects the source repository configuration. If that repository has gpgcheck=0, reposync does not override it. Check the repository definition before treating the synchronization as signature-verified.
DNF creates a directory named after the repository ID unless you pass --norepopath:
/srv/repos/
└── <repo-id>/
├── repodata/
└── <downloaded RPM paths>The repodata/ directory must exist when you use --download-metadata. RPMs might appear under Packages/, directly under the repository root, or in another directory structure inherited from the source repository.
On Rocky Linux 10.2, the tested extras sync used a Packages/ tree and completed in under a minute at about 67 MB. BaseOS or AppStream are far larger—plan disk space accordingly.
Pick the REPO_ID from dnf repolist on your own system. Do not mix example IDs from RHEL, Rocky, AlmaLinux, and Oracle Linux in one command.
Sync Multiple Repositories and Architectures
Mirror several repositories in one run by repeating --repoid:
sudo dnf reposync \
--repoid=baseos \
--repoid=appstream \
--download-metadata \
--gpgcheck \
--delete \
--download-path=/srv/reposReplace baseos and appstream with the exact IDs from your dnf repolist --all output.
| Option | Purpose |
|---|---|
--download-metadata |
Keep upstream repository metadata |
--delete |
Remove local RPMs no longer present upstream |
--arch=x86_64 |
Limit to one CPU architecture |
--arch=noarch |
Include architecture-independent packages |
--gpgcheck |
Check downloaded RPM signatures when the source repository has gpgcheck=1; remove failures and return exit status 1 |
--newest-only |
Download only the newest version of each package |
--norepopath |
Write RPMs directly under the download path without a repo-ID subdirectory |
An architecture-filtered mirror usually needs both the target architecture and noarch.
--delete makes the local tree match upstream. That is what you want for a current mirror, but it can remove older RPMs that air-gapped systems still depend on. Keep snapshots when you need historical versions.
Verify the Downloaded Repository
Check repository layout
Confirm repodata/ exists and RPMs were downloaded:
find /srv/repos/extras -maxdepth 2 -type dSample output on Rocky Linux 10.2 (extras uses a Packages/ tree):
/srv/repos/extras
/srv/repos/extras/repodata
/srv/repos/extras/Packages
/srv/repos/extras/Packages/l
/srv/repos/extras/Packages/aList a few RPM paths—layout varies by upstream repository:
find /srv/repos/extras -type f -name '*.rpm' | headSample output (Rocky Linux 10.2 layout):
/srv/repos/extras/Packages/l/livesys-scripts-0.9.2-1.el10.0.1.noarch.rpm
/srv/repos/extras/Packages/a/anaconda-live-40.22.3.46-1.el10.rocky.0.6.noarch.rpm
/srv/repos/extras/Packages/r/rocky-backgrounds-extras-100.5-3.el10.noarch.rpmCheck how much space the mirror uses:
du -sh /srv/repos/extrasSample output:
67M /srv/repos/extrasYou must see repodata/ and at least one downloaded RPM. Do not assume that every repository uses a Packages/ directory.
Verify package signatures
Pick any downloaded RPM and check its signature. On Rocky Linux 10.2, packages landed under Packages/:
rpm --checksig /srv/repos/extras/Packages/l/livesys-scripts-0.9.2-1.el10.0.1.noarch.rpmSample output:
/srv/repos/extras/Packages/l/livesys-scripts-0.9.2-1.el10.0.1.noarch.rpm: digests signatures OKTest the repository directly
Ask DNF to read the synced tree without editing .repo files permanently:
sudo dnf \
--repofrompath=offline,file:///srv/repos/extras \
--repoid=offline \
list availableSample output (trimmed):
Added offline repo from file:///srv/repos/extras
Available Packages
anaconda-live.noarch 40.22.3.46-1.el10.rocky.0.6 offline
epel-release.noarch 10-7.el10_1 offline
rocky-release-core.noarch 10-1.el10 offlineIf this listing works, the metadata from --download-metadata is usable.
Check group and modular metadata
AppStream and many product repositories ship group or module metadata. Before you move a mirror into a disconnected network, install a module or group you rely on in a test VM pointed at the synced copy.
Configure Offline Clients to Use the Repository
Local filesystem or removable storage
Mount the synced tree and point a .repo file at it. Check which GPG keys your distribution installed before you copy a key path from any example:
ls -1 /etc/pki/rpm-gpg/Sample output on Rocky Linux 10.2:
RPM-GPG-KEY-EPEL-10
RPM-GPG-KEY-Rocky-10
RPM-GPG-KEY-Rocky-10-TestingRocky Linux 10 uses the versioned RPM-GPG-KEY-Rocky-10 key—the older unversioned RPM-GPG-KEY-rockyofficial path belongs to earlier releases. Use the key path that matches your distribution and major version.
[offline-extras]
name=Offline Rocky Linux 10 Extras
baseurl=file:///mnt/repos/extras
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-10HTTP repository server
Copy synchronized repositories under your web root—one directory per major release, architecture, and repository ID:
/var/www/html/repos/
├── el8/
│ └── x86_64/
├── el9/
│ └── x86_64/
└── el10/
└── x86_64/Clients use an HTTP baseurl. Keep gpgcheck=1 even for internal mirrors.
For Apache layout, SSL, and client .repo examples, see configure a local offline DNF repository over HTTP instead of duplicating that walkthrough here.
Update and Maintain the Repository Mirror
reposync skips packages that are already present locally, so updates are incremental. Refresh a mirror with:
sudo dnf reposync \
--repoid="$REPO_ID" \
--download-metadata \
--gpgcheck \
--delete \
--download-path="$DEST"Practices that keep production mirrors sane:
- Schedule syncs with a systemd timer or cron on the connected host
- Sync into a staging directory first; swap only after verification
- Re-check signatures and
repodata/before promoting a new tree - Keep dated snapshots when offline systems may need superseded RPMs
- Monitor free disk space—repository growth is not always predictable
- Copy updates into the air-gapped network through approved media
- Separate directories by major release and architecture
For large estates, Red Hat Satellite, Pulp, or vendor mirroring platforms may replace hand-rolled reposync jobs. This article stays focused on direct dnf reposync on a connected host.
Troubleshoot dnf reposync Problems
| Symptom | Likely cause | What to try |
|---|---|---|
No such command: reposync |
Plugin package missing | Install dnf-plugins-core (or dnf5-plugins on DNF5 hosts) |
| Repository ID not found | Wrong or disabled repo ID | dnf repolist --all; enable the repo if needed |
| RHEL repository unavailable | Registration or content access | Verify subscription and enabled repositories |
Cannot download repomd.xml |
DNS, proxy, TLS, or network | Test the mirror URL; check proxy settings |
| GPG verification failure | Bad signature or wrong key | Confirm repository GPG keys and source integrity |
| Offline client reports missing RPM | Metadata references undownloaded package | Perform a complete metadata-preserving resync. Regenerate metadata only for an intentionally custom repository, then test dependencies, groups, and modules. |
| Modules cannot be installed | Modular metadata missing | Resync with --download-metadata |
| Disk fills during sync | Repository larger than expected | Check du and df before syncing BaseOS/AppStream |
| Old packages disappear | --delete matched upstream |
Restore from snapshot or keep archival mirrors |
| Wrong release packages | Incorrect repo definition | Verify distro release, repo ID, and architecture |
| Duplicate repo directory | Default repo-ID path layout | Review destination path and --norepopath |
Legacy RHEL and CentOS 7 Note
CentOS Linux 7 is end of life. RHEL 7 used yum-utils and a different reposync syntax with separate entitlement steps. The workflow in this article targets maintained DNF-based releases—RHEL 8 and later, Rocky Linux, AlmaLinux, CentOS Stream, and current Oracle Linux.
If you still operate EL7 systems, plan a migration to a supported major release rather than extending the old mirror procedure.
References
- Red Hat — Mirror a full repository for disconnected RHEL systems
- Oracle Linux — Setting up a local yum mirror
- DNF reposync plugin documentation
- createrepo_c project
Summary
The current offline-mirror workflow on RHEL-family Linux is straightforward:
- Install
dnf-plugins-core(andcreaterepo_cif you will customize trees). - Find exact repository IDs with
dnf repolist --all. - Enable repositories using your distribution’s normal mechanism.
- Run
dnf reposync --download-metadata --gpgcheck. - Verify RPM signatures and
repodata/. - Copy the tree or publish it over HTTP for offline clients.
- Re-run
reposyncon a schedule to keep the mirror current.
You normally do not need createrepo_c after a metadata-preserving sync. Use it when you change the package set yourself.

