sysadmin · June 10, 2026 · 14 min read

dnf, rpm, and a local repo.
packages, groups, and an offline mirror.

dev1 needs monitoring tools installed and a local DNF repository built from a directory of RPMs, ready for an air-gapped replica that will have no internet repos.

// what we’re getting into
  1. The ticket
  2. Concept review. dnf and rpm.
  3. Install the monitoring tools
  4. Install a package group
  5. Which package owns a file
  6. Build the local repo mirror
  7. dnf history and provides
  8. Exam questions
  9. Final checklist: confirm everything works

The ticket: dev1 wants htop, tree, nmap, bind-utils, and the Development Tools group installed. And, because a hardened replica is going into a DMZ next month with no internet repos, they want a local DNF repository built now from a directory of RPMs, with the workflow documented. Bonus: figure out which package owns /usr/sbin/sshd.

The real problem: installing packages is easy. Building a working repository from scratch is the part the exam tests, and it is the part that saves you when there is no internet.

What we are doing: using dnf and rpm confidently, then building and testing a local repo.

The ticket

bash
sudo dnf repolist
sudo subscription-manager status

Concept review. dnf and rpm.

bash
man dnf rpm createrepo

dnf is the high level package manager. It resolves dependencies and talks to repositories. rpm is the lower level tool for querying what is already installed.

dnf
dnf install pkg          install
dnf remove pkg           remove
dnf search keyword       find by keyword
dnf provides /path       which package owns a file
dnf group list           list package groups
dnf group install NAME   install a group
dnf history              transaction log, with undo
rpm queries
rpm -q pkg        is it installed
rpm -qa          list everything installed
rpm -ql pkg      files a package installed
rpm -qf /path    which package owns this file
rpm -qc pkg      config files only

Install the monitoring tools

bash
sudo dnf install -y htop tree nmap bind-utils
rpm -q htop tree nmap bind-utils
which htop tree nmap dig

Install a package group

Groups bundle related packages. Development Tools pulls in the compiler toolchain.

bash
sudo dnf group list | head -20
sudo dnf group info "Development Tools"
sudo dnf group install -y "Development Tools"
rpm -q gcc make rpm-build

Which package owns a file

The bonus ask, and a genuinely useful skill during audits.

bash
rpm -qf /usr/sbin/sshd        # openssh-server-...
rpm -ql openssh-server | head -10
rpm -qc openssh-server        # its config files

Build the local repo mirror

This is the exam scenario. Install the repo tool, download RPMs into a directory without installing them, generate the metadata, then write a repo file that points at the directory. Finally, prove you can install from that repo alone.

bash
sudo dnf install -y createrepo_c
sudo mkdir -p /srv/repo_mirror

# download RPMs plus dependencies without installing
sudo dnf install -y --downloadonly --downloaddir=/srv/repo_mirror htop tree

# generate the repository metadata
sudo createrepo /srv/repo_mirror/

# write the .repo file pointing at the directory
sudo bash -c 'cat > /etc/yum.repos.d/server1-local.repo << "EOF"
[server1-local]
name=Server1 Local Mirror
baseurl=file:///srv/repo_mirror
enabled=1
gpgcheck=0
EOF'

sudo dnf clean all
sudo dnf repolist    # server1-local should appear

# prove it: install using ONLY the local repo
sudo dnf remove -y tree
sudo dnf --disablerepo='*' --enablerepo='server1-local' install -y tree
rpm -q tree

dnf history and provides

Two more tools worth reflexes. History lets you undo a transaction that broke something. Provides finds the package for a command you do not have yet.

bash
sudo dnf history | head -10
sudo dnf history info 1
# sudo dnf history undo last   # reverses the last transaction

sudo dnf provides /usr/bin/htpasswd    # -> httpd-tools
sudo dnf provides '*/semanage'         # -> policycoreutils-python-utils

Exam questions

Write the command first.

Q1. Find which installed package provides /etc/ssh/sshd_config.

Q2. Configure a local DNF repository called localrepo from packages in /packages, GPG check off, enabled by default.

Q3. Without internet repos, install httpd using only a local repo built from RPMs in /tmp/rpms.

Answers.

bash
# A1
rpm -qf /etc/ssh/sshd_config    # openssh-server-...
# A2
sudo bash -c 'cat > /etc/yum.repos.d/localrepo.repo << EOF
[localrepo]
name=Local Repo
baseurl=file:///packages
enabled=1
gpgcheck=0
EOF'
sudo dnf clean all; sudo dnf repolist
# A3
sudo createrepo /tmp/rpms
# write a .repo with baseurl=file:///tmp/rpms, then:
sudo dnf --disablerepo='*' --enablerepo='tmprepo' install -y httpd

Final checklist: confirm everything works

If every check passes, the ticket is done.

bash
# 1. all requested tools installed
rpm -q htop tree nmap bind-utils gcc

# 2. sshd ownership identified
rpm -qf /usr/sbin/sshd

# 3. local repo shows in repolist and can install offline
sudo dnf repolist | grep server1-local

# 4. all three exam commands written from scratch
# 5. tracker entry checked off

Reply to dev1: all tools installed. Local mirror at /srv/repo_mirror with server1-local.repo. For offline installs, —disablerepo=’*’ —enablerepo=‘server1-local’. And /usr/sbin/sshd belongs to openssh-server.

next post
A backup pipeline with tar, rsync, and scp