DevOps · K8s · Volleyball · Travel  •  DevOps · K8s · Volleyball · Travel  •  DevOps · K8s · Volleyball · Travel
Explore NY Stream

How To Setup Local YUM Server Repository In RedHat Enterprise Linux

— ny_wk

How To Setup Local YUM Server Repository In RedHat Enterprise Linux

A local YUM repository lets a Red Hat Enterprise Linux or CentOS machine install and update RPM packages from your own disk or LAN instead of the internet, which is faster, works in air-gapped networks, and keeps a fleet of servers perfectly version-aligned. This guide walks through two proven approaches: a filesystem repo built straight from the install DVD/ISO, and a shared HTTP repo built with createrepo and served by Apache to many client machines.

Why Build a Local YUM Repository?

YUM (Yellowdog Updater, Modified) is the classic dependency-resolving package manager for RPM-based distributions. It reads repository metadata, calculates which packages and dependencies are needed, and installs them in the correct order so you never have to chase rpm dependencies by hand. On current releases the YUM command is provided by DNF (the modern rewrite of YUM), but the configuration files, repo layout, and workflow described here are identical.

There are several solid reasons to host packages locally:

  • No internet access: secured data centers, DMZ hosts, and air-gapped labs often cannot reach Red Hat's CDN, so a local mirror is the only practical source.
  • Speed and bandwidth: pulling RPMs over gigabit LAN is far quicker than the public internet, and you download each package once instead of once per server.
  • Consistency: every machine installs the exact same package versions, which eliminates "works on one box, breaks on another" drift.
  • Control: you decide precisely which packages and updates are available, ideal for change-controlled or compliance-bound environments.
  • Offline subscription independence: a media-based repo gives you a usable package source even before a system is registered with a subscription.

There are two shapes a local repository can take. A filesystem (file://) repo is private to a single host and is perfect for a quick fix or a standalone server. A network (http://) repo is published over the LAN so dozens of clients share one mirror. The sections below cover both.

Method 1: Build a Filesystem Repo From the Install ISO

The fastest local YUM repository comes from the installation media you already have. The DVD/ISO ships with a complete set of base RPMs plus a ready-made repodata directory, so on most releases you do not even need to regenerate metadata.

Step 1 — Mount the ISO or DVD

If you have a physical DVD, or an ISO file copied to the server, mount it read-only on a directory of your choosing:

  • Create a mount point: mkdir -p /mnt/iso
  • Mount a physical disc: mount /dev/sr0 /mnt/iso
  • Or mount an ISO file via loopback: mount -o loop /root/rhel.iso /mnt/iso

Verify the contents with ls /mnt/iso. You should see a BaseOS and AppStream directory (RHEL 8/9) or a Packages directory and a repodata folder (RHEL/CentOS 6/7).

Step 2 — Make the mount permanent (optional but recommended)

A plain mount is lost on reboot. To copy the media to local disk so the repo always survives, create a target directory and copy everything across, preserving permissions:

  • mkdir -p /repo/rhel
  • cp -a /mnt/iso/. /repo/rhel/

The -a (archive) flag copies recursively while preserving ownership, timestamps, and symlinks. Alternatively, add a line to /etc/fstab so the ISO re-mounts automatically: /root/rhel.iso /mnt/iso iso9660 loop,ro 0 0, then run mount -a to apply it immediately.

Step 3 — Create the .repo file

Repository definitions live in /etc/yum.repos.d/ as files ending in .repo. Move any existing repo files aside first so they do not interfere on an offline box:

  • mkdir /root/repo-backup && mv /etc/yum.repos.d/*.repo /root/repo-backup/

Then create /etc/yum.repos.d/local.repo. On RHEL/CentOS 8 and 9 the media exposes two repositories, BaseOS and AppStream, so define both:

[LocalBaseOS]
name=Local Base OS
baseurl=file:///repo/rhel/BaseOS
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

[LocalAppStream]
name=Local AppStream
baseurl=file:///repo/rhel/AppStream
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

On older RHEL/CentOS 6/7 there is a single repo and one base URL, for example baseurl=file:///repo/rhel. The triple slash in file:/// is correct: two slashes belong to the file:// scheme and the third is the leading slash of the absolute path.

Step 4 — Clean the cache and confirm

  • dnf clean all (or yum clean all on older systems) flushes stale metadata.
  • dnf repolist should now list LocalBaseOS and LocalAppStream with a package count.

If the repolist shows packages, your filesystem repository is live and you can install with dnf install <package> entirely offline.

Method 2: Build an HTTP Repo With createrepo for Many Clients

When you need to serve packages to several machines, publish the repository over HTTP using Apache (httpd). This is the standard pattern for patching a fleet from one internal mirror. The server hosts the RPMs and the generated metadata; every client simply points its baseurl at the server's web address.

Step 1 — Install the tooling on the repo server

You need two things: a metadata generator and a web server. The createrepo_c package (a fast C rewrite of the original createrepo) builds the repodata directory; httpd serves it.

  • dnf install -y createrepo_c httpd

On a system with no internet, install these RPMs from the ISO repo you built in Method 1, or with rpm -ivh from the media's Packages/BaseOS directory, resolving any dependencies first. The command is createrepo on legacy systems and either createrepo or createrepo_c on modern ones.

Step 2 — Stage the packages

Apache serves files from /var/www/html by default, so create a directory there and gather every RPM you want to publish:

  • mkdir -p /var/www/html/repo
  • Copy RPMs into it, for example from the mounted ISO: cp /mnt/iso/BaseOS/Packages/*.rpm /var/www/html/repo/
  • Add any extra third-party or custom RPMs to the same directory.

Step 3 — Generate the repository metadata

Run createrepo_c against the package directory. The --database flag also builds SQLite databases that speed up dependency resolution:

  • createrepo_c --database /var/www/html/repo

This creates a repodata subdirectory containing repomd.xml and the compressed primary, filelists, and other-data XML files. Any time you add, remove, or change RPMs you must regenerate the metadata. For an existing repo use the faster incremental update: createrepo_c --update /var/www/html/repo.

Step 4 — Start and enable Apache

  • systemctl enable --now httpd

Test from the server itself with curl http://localhost/repo/ — you should see a directory listing that includes repodata/. The repository is now reachable at http://<server-ip-or-hostname>/repo/.

Client Configuration

Each client that should consume the HTTP repository needs a matching .repo file. Configure a client in the following order:

  1. Create the file /etc/yum.repos.d/local-http.repo on the client.
  2. Add a repository stanza pointing at the server. A minimal definition needs an ID in brackets, a human-readable name, and a baseurl:
    • [local-http]
    • name=Local HTTP Repository
    • baseurl=http://192.168.1.10/repo/
    • enabled=1
    • gpgcheck=0
  3. Refresh the cache so the client reads the new metadata: dnf clean all && dnf makecache.
  4. Confirm the repo is recognized: dnf repolist.
  5. Install a test package to prove end-to-end delivery: dnf install zsh.

Replace 192.168.1.10 with your server's real IP or hostname, and ensure the trailing path matches the directory you ran createrepo_c against (the one that now contains repodata/). Set gpgcheck=1 with a valid gpgkey once you have signed packages or trust the media key. You can roll this single file out to an entire fleet with a configuration-management tool, or host the file itself on the web server and add it with dnf config-manager --add-repo http://<server>/local-http.repo.

Useful repository directives

DirectiveWhat it does
nameHuman-readable label shown in repolist output.
baseurlLocation of the repo: file://, http://, or ftp://.
enabled1 uses the repo; 0 ignores it until enabled.
gpgcheck1 verifies package signatures; 0 disables the check.
gpgkeyPath or URL to the public GPG key used for verification.
priorityLower number wins when a package exists in multiple repos.

Common Pitfalls and How to Avoid Them

Most local-repo problems trace back to four areas: GPG keys, SELinux, the firewall, and stale metadata. Work through these before assuming a deeper fault.

GPG key errors

If installs fail with Public key for package is not installed, the package signature cannot be verified. Either import the correct key with rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release (Red Hat's keys ship under /etc/pki/rpm-gpg/), or, for unsigned in-house RPMs, set gpgcheck=0 in that repo's stanza. Disabling the check is fine for trusted internal packages but should not be the default for production.

SELinux blocking Apache

SELinux denies httpd access to files that lack the right context. If clients get 403 errors, restore the proper context on the web root: restorecon -Rv /var/www/html/repo. If you placed the repo outside the default web root, label it with semanage fcontext -a -t httpd_sys_content_t "/path(/.*)?" followed by restorecon -Rv /path. Confirm SELinux state with getenforce before troubleshooting elsewhere.

Firewall blocking HTTP

By default the firewall will not pass web traffic. Open the HTTP service on the repo server and reload:

  • firewall-cmd --permanent --add-service=http
  • firewall-cmd --reload

From a client, verify reachability with curl http://<server>/repo/repodata/repomd.xml. If that returns XML, the network path is clear and the problem lies in the repo config rather than connectivity.

Stale metadata after changes

YUM/DNF caches metadata aggressively. After you add or remove RPMs and rerun createrepo_c --update, clients keep serving old data until told otherwise. Always pair a metadata regeneration on the server with dnf clean all followed by dnf makecache on the clients. Forgetting this step is the single most common reason a freshly added package "isn't found."

Other gotchas

  • Wrong baseurl path: the baseurl must point at the directory that contains repodata/, not at the repodata folder itself.
  • file:// triple slash: a local URL needs three slashes — file:///repo/rhel, not file://repo/rhel.
  • Permissions: ensure the RPMs and repodata are world-readable so Apache and DNF can read them (chmod -R a+rX).

Verification

Confirm the repository works end to end with a short checklist on the client:

  1. List active repos and package counts: dnf repolist. Your local repo should appear with a non-zero count.
  2. Inspect detail, including the resolved baseurl: dnf repolist -v or dnf repoinfo local-http.
  3. Search the repo for a known package: dnf --disablerepo="*" --enablerepo="local-http" list available | head.
  4. Perform a real install to prove delivery and dependency resolution: dnf install -y tree, then run tree --version to confirm it landed.

If the install pulls the package from your local repo (the source repo name appears in the transaction summary) and completes without reaching the internet, the local YUM repository is fully operational.

Key Takeaways

  • Two repo types: a file:// filesystem repo built from the install ISO serves a single host; an http:// repo served by Apache serves an entire fleet.
  • createrepo_c builds the metadata — run it on any directory of RPMs, and rerun --update every time the package set changes.
  • The .repo file is the contract: a valid stanza needs at minimum an ID, name, and baseurl pointing at the directory that holds repodata/.
  • Refresh after every change with dnf clean all and dnf makecache, or clients keep using stale metadata.
  • Check GPG, SELinux, and the firewall first when installs or HTTP access fail — they cause most local-repo issues.

Frequently Asked Questions

What is the difference between YUM and DNF?

DNF is the next-generation rewrite of YUM with faster dependency resolution and a cleaner API. On RHEL/CentOS 8 and later, the yum command is a symlink to DNF, so both work. The repository files, layout, and createrepo workflow are unchanged, which is why the same steps apply across versions.

Do I have to run createrepo on a repo built from the ISO?

Usually no. Installation media ships with a pre-built repodata directory, so you can point baseurl straight at it. You only need createrepo_c when you assemble your own directory of RPMs or add packages to an existing repo.

Why does my client say "no package available" after I added an RPM?

The metadata is stale. Regenerate it on the server with createrepo_c --update /var/www/html/repo, then on the client run dnf clean all followed by dnf makecache. DNF caches the old metadata until it is explicitly refreshed.

How do I serve the repository to many machines without HTTP?

FTP and NFS both work. Set baseurl=ftp://<server>/repo/ for an FTP mirror, or export the directory over NFS, mount it on each client, and use a file:// URL against the mount point. HTTP via Apache remains the most common and firewall-friendly choice.

For more Linux and system administration walkthroughs, subscribe to @explorenystream on YouTube.