Download and Sync an Entire DNF Repository for Offline Use

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 extras repository with dnf reposync --download-metadata, confirmed repodata/ 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 reposync copies RPM packages from an enabled remote repository into a local directory.
  • --download-metadata copies the upstream repository metadata too—package indexes, groups, and modular data when the source provides it.
  • createrepo_c builds 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.

WARNING

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 usually noarch alongside 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:

bash
sudo dnf install -y dnf-plugins-core createrepo_c

dnf-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:

bash
dnf reposync --help

The help text should list --download-metadata, --delete, --download-path, and related flags.

bash
createrepo_c --version

Sample output:

output
Version: 1.1.2 (Features: LegacyWeakdeps )

Find and Enable Repository IDs

Start with the repositories already enabled on your mirror host:

bash
sudo dnf repolist

Sample output on Rocky Linux 10.2:

output
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 - Extras

Include disabled repositories you plan to mirror:

bash
sudo dnf repolist --all

Sample output (trimmed):

output
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           disabled

Use 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:

bash
REPO_ID=extras
DEST=/srv/repos

Run the sync:

bash
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:

output
/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:

bash
sudo dnf reposync \
  --repoid=baseos \
  --repoid=appstream \
  --download-metadata \
  --gpgcheck \
  --delete \
  --download-path=/srv/repos

Replace 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:

bash
find /srv/repos/extras -maxdepth 2 -type d

Sample output on Rocky Linux 10.2 (extras uses a Packages/ tree):

output
/srv/repos/extras
/srv/repos/extras/repodata
/srv/repos/extras/Packages
/srv/repos/extras/Packages/l
/srv/repos/extras/Packages/a

List a few RPM paths—layout varies by upstream repository:

bash
find /srv/repos/extras -type f -name '*.rpm' | head

Sample output (Rocky Linux 10.2 layout):

output
/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.rpm

Check how much space the mirror uses:

bash
du -sh /srv/repos/extras

Sample output:

output
67M	/srv/repos/extras

You 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/:

bash
rpm --checksig /srv/repos/extras/Packages/l/livesys-scripts-0.9.2-1.el10.0.1.noarch.rpm

Sample output:

output
/srv/repos/extras/Packages/l/livesys-scripts-0.9.2-1.el10.0.1.noarch.rpm: digests signatures OK

Test the repository directly

Ask DNF to read the synced tree without editing .repo files permanently:

bash
sudo dnf \
  --repofrompath=offline,file:///srv/repos/extras \
  --repoid=offline \
  list available

Sample output (trimmed):

output
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                       offline

If 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:

bash
ls -1 /etc/pki/rpm-gpg/

Sample output on Rocky Linux 10.2:

output
RPM-GPG-KEY-EPEL-10
RPM-GPG-KEY-Rocky-10
RPM-GPG-KEY-Rocky-10-Testing

Rocky 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.

ini
[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-10

HTTP repository server

Copy synchronized repositories under your web root—one directory per major release, architecture, and repository ID:

output
/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:

bash
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


Summary

The current offline-mirror workflow on RHEL-family Linux is straightforward:

  1. Install dnf-plugins-core (and createrepo_c if you will customize trees).
  2. Find exact repository IDs with dnf repolist --all.
  3. Enable repositories using your distribution’s normal mechanism.
  4. Run dnf reposync --download-metadata --gpgcheck.
  5. Verify RPM signatures and repodata/.
  6. Copy the tree or publish it over HTTP for offline clients.
  7. Re-run reposync on 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.


Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)