3 of 3

Package and RPM Database RepairLesson 3 of 3

Repositories, dnf failures and rolling back

Diagnose repository, metadata, GPG and dependency errors, build a local repository, and undo or redo a transaction.

  • dnf repolist
  • dnf repoquery
  • dnf clean all
  • dnf makecache
  • dnf history
  • dnf history undo
  • dnf distro-sync
  • dnf check
  • dnf config-manager
  • dnf download
  • createrepo_c
  • dnf needs-restarting
  • dnf versionlock

50 min3 exercises

After this lesson you can

  • Read a repository definition and test each of its settings from the command line.
  • Map the common dnf error messages to a cause: URL, metadata, GPG, dependency, space, lock.
  • Create a local repository, install from it, and use dnf history to inspect and undo a transaction.

The idea

dnf is a resolver sitting on top of repositories and rpm. A repository is a directory of .rpm files plus a repodata/ directory describing them (names, versions, dependencies, checksums). dnf downloads that description (the metadata), works out which packages satisfy your request, downloads them, checks their signatures, and hands them to rpm as a single transaction.

Three layers can fail, and error messages belong to one of them:

1  Reaching the repository    URL, DNS, network, TLS, mounting a source          "Cannot download repomd.xml"
2  Trusting what it says      metadata checksum, GPG signature, subscription      "GPG check FAILED", "Public key ... not installed"
3  Satisfying the request     dependencies, conflicts, disk space, scriptlets     "nothing provides", "conflicts with", "Insufficient space"

Fix in that order. A dependency error is meaningless while the metadata cannot be downloaded.

A repository definition

[labrepo]
name=Lab repository
baseurl=file:///srv/repo
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-lab
SettingMeaning
[labrepo]Repository id, used in --enablerepo/--disablerepo
baseurl / metalink / mirrorlistWhere the repository is. baseurl may be http://, https://, ftp:// or file://
enabled1 uses it, 0 ignores it unless asked for
gpgcheckVerify package signatures
repo_gpgcheckVerify the signature of the metadata too
gpgkeyWhere the public key is
sslverify, sslclientcert, sslclientkeyTLS settings; Red Hat’s CDN uses a client certificate from the subscription
priority, exclude, includepkgsPrefer or limit what a repository provides

RHEL repositories are enabled by a subscription (registration with subscription-manager) and recorded in /etc/yum.repos.d/redhat.repo. On an unregistered system, that file is empty and dnf finds nothing; the fix is to register, or to use local repositories.

The repository tools

dnf repolist [--all]                         # enabled (or all) repositories
dnf repoinfo REPOID                          # details for one
dnf makecache                                # download metadata now (shows errors clearly)
dnf clean all                                # drop caches: fixes stale or corrupt metadata
dnf provides /usr/bin/foo                    # which package provides this file or capability
dnf repoquery --whatprovides foo.so.1        # same, from repository metadata
dnf repoquery --whatrequires PKG             # who depends on PKG
dnf download PKG                             # fetch the .rpm only

Making a local repository

Anything that reads a directory can be a repository. Create the metadata with createrepo_c:

sudo dnf install -y createrepo_c
sudo mkdir -p /srv/repo
sudo cp *.rpm /srv/repo/
sudo createrepo_c /srv/repo         # writes /srv/repo/repodata/
ls /srv/repo/repodata
repomd.xml  <hash>-filelists.xml.gz  <hash>-primary.xml.gz  <hash>-other.xml.gz

repomd.xml is the index of the others: if it is missing, the repository does not exist as far as dnf is concerned. After adding packages, run createrepo_c --update /srv/repo. For a signed repository, sign the packages with rpmsign and publish the key.

What the errors mean

Message (abridged)LayerCauseFix
Failed to download metadata for repo 'x', Cannot prepare internal mirrorlist, Could not resolve host1URL, DNS or networkTest the URL with curl -I; fix baseurl
Status code: 404 for .../repomd.xml1Wrong path, or no repodata/createrepo_c; check the directory
Curl error (60): SSL certificate problem1Untrusted or expired certificateInstall the CA; fix the clock (timedatectl)
Repository 'x' is missing name in configuration1Syntax in .repo fileAdd name=
Metadata file does not match checksum2Stale cache or mirror out of syncdnf clean all, makecache
Public key for x.rpm is not installed2Key not importedrpm --import the right key
GPG check FAILED2Package altered or signed with another keyDo not install; obtain it again from a trusted source
nothing provides X needed by Y3Dependency in a repo that is disabled or not presentFind with dnf provides X; enable that repo
package A conflicts with B3Two packages that cannot coexistRemove one, or choose --allowerasing knowingly
Insufficient space in download directory3/var is fulldf -h /var, dnf clean all, free space
Transaction failed: Rpm transaction failed3A scriptlet failed or a file conflictsRead the rpm lines above it; rpm -q --scripts
Error: Failed to ... waiting for process with pid N to finishAnother dnf is runningWait. ps -p N; remove nothing

Try it

Seeing the failure properly

sudo dnf clean all
sudo dnf -v makecache
Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried
Error: Failed to download metadata for repo 'labrepo': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried

That is layer 1: either baseurl is wrong or repodata/ is missing. Check the first, then the second:

grep baseurl /etc/yum.repos.d/labrepo.repo
ls /srv/repo/repodata/repomd.xml

Transactions and history

Every dnf install, update and remove is a numbered transaction in /var/lib/dnf/history.sqlite.

sudo dnf history
sudo dnf history info 12
ID     | Command line             | Date and time    | Action(s)      | Altered
-------------------------------------------------------------------------------
    14 | install -y tree          | 2026-11-04 12:10 | Install        |    1
    13 | update                   | 2026-11-04 09:02 | Upgrade        |   18
    12 | install createrepo_c     | 2026-11-03 17:44 | Install        |    3

dnf history info ID lists the exact packages, versions and the user. You can reverse or repeat a transaction:

sudo dnf history undo 14       # reverse transaction 14
sudo dnf history redo 14       # repeat it
sudo dnf history rollback 12   # return the package set to its state after transaction 12

undo works when the previous versions are still available in an enabled repository; a downgrade of core packages (glibc, systemd) is risky and may be unsupported. After a bad update the safer paths are, in order: dnf history undo for a single transaction, dnf downgrade PKG-VERSION, dnf distro-sync (make everything match the enabled repositories exactly), and a snapshot or backup restore.

Checking afterwards

sudo dnf check
sudo dnf needs-restarting -r

dnf check verifies the dependency state of the installed set and prints nothing when it is consistent. dnf needs-restarting -r says whether a reboot is needed (a new kernel, glibc, systemd), and -s lists services that should be restarted.

Going further

Controlling updates. dnf versionlock add PKG (package python3-dnf-plugin-versionlock) pins a version; exclude=PKG* in a repository or /etc/dnf/dnf.conf skips it. dnf update --security applies only security errata. Beyond the exam: Red Hat Satellite or a local mirror (reposync) is how production fleets control what each machine sees.

Practice

0 of 3 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    Read a repository definition

    List the enabled repositories. For one of them, print its definition and answer: what is the baseurl or metalink, is gpgcheck on, is it enabled, and what do sslverify and sslclientcert (if present) refer to?

    Check your result
    • dnf repolist lists the repo ids.
    • dnf repoinfo REPOID (or grep -A12 '^\[REPOID\]' /etc/yum.repos.d/*.repo) shows the settings.
    • You can say which file defines it (grep -l REPOID /etc/yum.repos.d/*.repo).
    Hint

    Repository files are in /etc/yum.repos.d/*.repo. A section header [id] starts each repository. dnf config-manager --dump REPOID prints the effective settings.

    Solution
    dnf repolist
    dnf repoinfo rhel-10-for-x86_64-baseos-rpms 2>/dev/null | head -20
    grep -l '^\[' /etc/yum.repos.d/*.repo
    dnf config-manager --dump | sed -n 1,25p
  2. Practice

    A local repository

    Make a repository from the packages in /srv/repo (put a few .rpm files there, for example from dnf download tree nano). Create the metadata, write a repository file with gpgcheck=0 only for this test, confirm dnf sees it, and install a package from it only (disable the other repositories for that one command).

    Check your result
    • ls /srv/repo/repodata/repomd.xml exists.
    • dnf repolist shows your repo id with a package count.
    • dnf --disablerepo='*' --enablerepo=labrepo install -y tree installs the package from labrepo.
    Hint

    createrepo_c /srv/repo writes repodata/. A repository file is a [id] section with name, baseurl=file:///srv/repo, enabled=1, gpgcheck=0. Run dnf clean all if the repository does not appear straight away.

    Solution
    sudo dnf install -y createrepo_c
    sudo mkdir -p /srv/repo && cd /srv/repo
    sudo dnf download --destdir /srv/repo tree nano
    sudo createrepo_c /srv/repo
    sudo tee /etc/yum.repos.d/labrepo.repo <<'EOT'
    [labrepo]
    name=Lab repository
    baseurl=file:///srv/repo
    enabled=1
    gpgcheck=0
    EOT
    sudo dnf clean all
    dnf repolist
    sudo dnf --disablerepo='*' --enablerepo=labrepo install -y tree
  3. Challenge

    Four failures, four causes

    Setup: edit /etc/yum.repos.d/labrepo.repo or the environment to cause these four failures, one at a time, read each message, repair it and run dnf makecache or the install again. Write for each: the message, the layer (URL, metadata, signature, dependency), and the fix.

    1  baseurl=file:///srv/repo-missing
    2  remove /srv/repo/repodata/repomd.xml
    3  gpgcheck=1  with no gpgkey for an unsigned package
    4  install a package whose dependency is in a disabled repository
    Check your result
    • 1: Cannot prepare internal mirrorlist / Failed to download metadata; fix the URL.
    • 2: repomd.xml not found; regenerate with createrepo_c.
    • 3: Public key for ... is not installed / GPG check FAILED; import the key or sign; do not disable the check for production.
    • 4: nothing provides ... needed by ...; enable the repository that provides it (dnf repoquery --whatprovides).
    Hint

    dnf -v makecache and journalctl -t dnf give more detail. dnf provides CAPABILITY and dnf repoquery --whatprovides CAPABILITY --disablerepo=... --enablerepo=... find which repository has a missing dependency.

    Solution
    sudo sed -i 's#^baseurl=.*#baseurl=file:///srv/repo-missing#' /etc/yum.repos.d/labrepo.repo
    sudo dnf makecache 2>&1 | tail -3
    sudo sed -i 's#^baseurl=.*#baseurl=file:///srv/repo#' /etc/yum.repos.d/labrepo.repo
    sudo mv /srv/repo/repodata/repomd.xml /root/ && sudo dnf makecache 2>&1 | tail -3
    sudo createrepo_c --update /srv/repo && sudo dnf makecache
    sudo sed -i 's/^gpgcheck=0/gpgcheck=1/' /etc/yum.repos.d/labrepo.repo
    sudo dnf --disablerepo='*' --enablerepo=labrepo install -y nano 2>&1 | tail -4
    sudo sed -i 's/^gpgcheck=1/gpgcheck=0/' /etc/yum.repos.d/labrepo.repo   # lab only
    sudo dnf repoquery --whatprovides libfoo.so.1 --enablerepo='*'