Roll back a bad dnf update on Rocky or Alma
An update broke something on Rocky or AlmaLinux 9. Use dnf history to find the transaction, undo it, and pin the package so it cannot come back.
Roll back a bad dnf update in the fewest steps
To roll back a bad dnf update, find the transaction ID that changed the package, read what that transaction did, then reverse it. Every dnf action is recorded with a number, so the recovery is a lookup rather than a guess. The commands here were written against Rocky Linux 9 and AlmaLinux 9, which ship dnf 4.
sudo dnf history list
sudo dnf history info 42
sudo dnf history undo 42Replace 42 with the ID you see in the first command. That is the short path, and it works only while the older packages are still available to download. Most of this guide is about the case where they are not, because that is the case the quick answers leave out.
Every dnf action gets a transaction ID
dnf writes each install, upgrade, downgrade, and removal into its own history database. dnf 4 keeps that database at /var/lib/dnf/history.sqlite. It is separate from the RPM database under /var/lib/rpm, which only knows what is installed right now. History knows the order in which things changed, and that order is what any rollback needs.
sudo dnf history list
sudo dnf history list --reverse
sudo dnf history list httpd
sudo dnf history info last
sudo dnf history info 42dnf history list httpd narrows the list to transactions that touched one package, so you can find the run that broke one service without reading everything. dnf history info describes a single transaction: which packages moved and in which direction, plus the command line that started it. Read that before you undo anything, because an undo reverses the whole transaction and not only the package you suspect.
Two log files hold the lower level detail. /var/log/dnf.log records what dnf did, and /var/log/dnf.rpm.log records the package changes as rpm applied them. Both matter when the transaction ran while you were asleep.
Undo one transaction with dnf history undo
dnf history undo performs the opposite of every action in one transaction. Packages it upgraded are downgraded back. Packages it downgraded are upgraded again. Packages it installed are removed. Packages it removed are installed again. Use it when you know which run broke the machine and nothing you did after that run matters.
sudo dnf history undo 42
sudo dnf history undo last
sudo dnf history undo --assumeno 42dnf 4 has no --dry-run, so the safe habit is to run the command without -y and read the plan it prints before you answer. --assumeno answers no for you, which shows the plan and changes nothing.
The undo is itself a transaction. It gets its own ID and appears in dnf history list, so an undo you regret can be undone in turn. That is also why the ID numbers keep climbing while the system moves backwards.
Roll back to a point in time with dnf history rollback
dnf history rollback undoes every transaction after the one you name, so the package set ends up as it was when that transaction finished. Use it when several runs happened between the last time the service worked and now, and you cannot tell which one is at fault.
sudo dnf history list --reverse
sudo dnf history rollback 40Two things to know first. The ID you pass is the last transaction you want to keep, not the first one you want to remove. And a rollback can refuse to run when the installed package set no longer matches what history expects, which happens after something was installed or removed with rpm directly, outside dnf. dnf does not guess in that situation, so read the message it prints, then fall back to undoing single transactions or downgrading one package.
A rollback that crosses a point release can move hundreds of packages at once. Read the size of the plan before you agree to it.
Downgrade a single package
Sometimes you do not want the transaction reversed. You want one package back at the version that worked, and you want the rest of the update kept.
sudo dnf list --showduplicates httpd
sudo dnf downgrade httpd
sudo dnf downgrade httpd-2.4.62-4.el9Run the first command before anything else. --showduplicates lists every version of that package the enabled repositories can still deliver. If the only version listed is the one you already have, no downgrade is possible from your repositories, and the next sections are the ones you need.
dnf downgrade httpd moves to the highest installable version below the one installed. Naming the exact version is better, because then you choose the target instead of accepting whatever is next in line. Afterwards check that the service came back with rpm -q httpd and systemctl status httpd, and confirm the port is listening again from outside the box.
Why an undo often cannot work
An undo is not a snapshot restore. dnf keeps no copy of the package it replaced. It reads history, works out which versions to put back, and then has to download those exact RPM files from a repository that still serves them. When nothing serves them, the transaction fails while dnf is resolving, before anything on disk changes.
A point release moved the tree. The Rocky and Alma 9 repositories carry the current minor version. When the next minor version ships, the previous builds leave the active repository and go to an archive. History still names the old version, and no enabled mirror offers it.
A third party repository rebuilt. Community repositories usually keep one current build per package, so yesterday's version is already gone. This bites most often with packages that came from EPEL or CRB rather than from the base repositories.
The repository is disabled or gone. A package installed from a repository you have since removed cannot be restored from it. The same is true of a package you installed from a local file.
The minor version reached end of life. Nothing is published for it any more, so there is nothing left to download at any version.
sudo dnf list --showduplicates answers the question for one package in one line. There is no command that pre-checks a whole transaction the same way, which is why an undo can fail during resolution after you thought you had a plan.
Fetch the old package from the vault
Both distributions keep archives of past minor versions. Add one as a disabled repository, download the single package you need, and downgrade to that file. Keeping it disabled matters, because an enabled archive repository can hold the whole machine on an old minor version and quietly stop your security updates.
For Rocky Linux, in /etc/yum.repos.d/rocky-vault.repo:
[rocky-9-5-baseos-vault]
name=Rocky Linux 9.5 BaseOS vault
baseurl=https://dl.rockylinux.org/vault/rocky/9.5/BaseOS/x86_64/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9
enabled=0AlmaLinux publishes its archive on a different host, in /etc/yum.repos.d/alma-vault.repo:
[alma-9-5-baseos-vault]
name=AlmaLinux 9.5 BaseOS vault
baseurl=https://vault.almalinux.org/9.5/BaseOS/x86_64/os/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-9
enabled=0Change 9.5 to the minor version you were running, and change BaseOS to AppStream when the package lives there. Confirm the key file name that is actually on your system with ls /etc/pki/rpm-gpg/, since that file is placed there by the release package rather than by you. Those base URLs were the published layout in September 2026. If a download fails, open the base URL in a browser: a 404 in the address bar is faster to read than a dnf resolution error. The two distributions differ in small ways like this, and the practical differences between Rocky Linux and AlmaLinux are worth knowing before you paste either file.
Then pull the package and install the file:
sudo dnf install -y dnf-plugins-core
sudo dnf --enablerepo=rocky-9-5-baseos-vault download --destdir=/root/rpm-rollback httpd
sudo dnf downgrade /root/rpm-rollback/httpd-2.4.62-4.el9.x86_64.rpmdnf download comes from the dnf-plugins-core package and is not part of the base command set, so install that first. Pass the file to dnf downgrade rather than to rpm -U, because that keeps dependency resolution inside dnf, which matters when the older package also needs an older library.
Keep your own copy of the old RPMs
The cheapest insurance is to stop throwing the packages away. Set keepcache in /etc/dnf/dnf.conf:
[main]
keepcache=1Downloaded RPMs then stay under /var/cache/dnf/, in a directory named after the repository. Find one and install it directly:
sudo find /var/cache/dnf -name 'httpd-*.rpm'
sudo dnf downgrade /var/cache/dnf/appstream-1ec8a0bfaa5e5ff9/packages/httpd-2.4.62-4.el9.x86_64.rpmThe cost is disk. The cache grows with every update and nothing trims it for you, so /var on a small VPS fills up eventually. sudo du -sh /var/cache/dnf shows how much is there and sudo dnf clean packages empties it. That second command also destroys your rollback material, so do not run it in the same week you are debugging an update. A cache check belongs on the maintenance checklist you run on a schedule.
The other habit is to download before you upgrade. It costs a few seconds and it gives you the exact files for the packages you care about:
sudo dnf download --destdir=/root/rpm-rollback httpd
sudo dnf upgradeWhen there is nothing left to download
Kernels are the exception, and the useful kind. Enterprise Linux 9 installs kernels side by side instead of replacing them, and installonly_limit in /etc/dnf/dnf.conf sets how many are kept. A bad kernel update therefore leaves a working kernel already on disk.
rpm -q kernel
grep installonly /etc/dnf/dnf.conf
sudo grubby --default-kernel
sudo grubby --info=ALL
sudo grubby --set-default=/boot/vmlinuz-5.14.0-503.14.1.el9_5.x86_64
sudo rebootUse the exact filename that grubby --info=ALL prints on your machine, not the one above. Setting the default before rebooting matters on a VPS, because the boot menu is not reachable over SSH and getting to it means opening the provider's console. After the reboot, uname -r shows which kernel you actually booted, and reading the kernel log on a VPS shows what the failing kernel was complaining about. Run rpm -q kernel first, because a previous cleanup can leave you with only the broken one, and then the fallback is installing an older kernel from the vault repository above.
For everything that is not the kernel, the fallback is a snapshot taken before the update. Rocky and Alma default to XFS, which has no snapshot feature of its own, and most cloud images put one XFS filesystem on one partition with no LVM under it. Check yours with lsblk and sudo vgs. In practice the rollback point is a provider snapshot of the whole disk, taken before you start the update and deleted once the machine has run for a day. If you want rollback as a built in property of the operating system rather than a habit you remember, that is a different design: image based server distributions version the whole system as one unit.
Stop the broken package coming straight back
An undo changes nothing in the repository, so the version you just removed is still the newest one there. The next dnf upgrade installs it again. Pin the package before you walk away.
sudo dnf install -y python3-dnf-plugin-versionlock
sudo dnf versionlock add httpd
sudo dnf versionlock list
sudo dnf versionlock delete httpdversionlock add httpd pins the version installed at that moment and writes the rule to /etc/dnf/plugins/versionlock.list. dnf then leaves that package alone until you delete the rule. The alternative, excludepkgs=httpd in dnf.conf or --exclude=httpd on one command, hides the package from dnf completely, which can make an unrelated transaction unsolvable when something else depends on it. For a package that is already installed, prefer versionlock.
A lock is also a decision to stop receiving security updates for that package. The lock file accepts # comments, so write the date and the reason into it, and set yourself a reminder somewhere you will actually see it. A forgotten lock is how a machine sits on a vulnerable web server for a year.
dnf 4 and dnf 5 are not the same tool
Rocky Linux 9 and AlmaLinux 9 ship dnf 4, and every command in this post was written against dnf 4. Fedora and newer releases ship dnf 5, which is a rewrite. It documents the same history subcommands, and it handles some options differently. Check which one you have, and check what your build accepts, before you trust a flag:
dnf --version
dnf history --helpdnf history --help is the honest answer for the machine in front of you. If you also run Debian or Ubuntu boxes, the map between apt and dnf commands covers the same ground there, where the equivalent move is apt install <package>=<version> followed by apt-mark hold, because apt records its runs in /var/log/apt/history.log but has no command that reverses one.
Why this matters if the machine updates itself
The reason to learn the history commands is that you let the server update itself. Unattended updates are the right default on a machine nobody logs into daily, and the trade is that changes land while you are not watching. Knowing the transaction model turns "something broke, no idea when" into a numbered list with times on it.
systemctl list-timers 'dnf-*'
sudo dnf history list
sudo dnf history info lastThe first command shows whether automatic runs are enabled and when the next one is due. Check it early, because there is no point spending an hour on a rollback that tonight's timer will reverse. Set those timers up deliberately, as in configuring dnf-automatic on Rocky and Alma, and lock anything you have rolled back.
One more check before you blame the package. An update can install correctly and still look broken, because a process is still running the old code and nothing restarted it. Work through which services still need a restart after an update before you undo anything. On a new machine, the update policy belongs in the same first pass as everything else in securing a new Rocky Linux server.
A drill worth running once
Run this on a machine that does not matter, today, while nothing is broken.
sudo dnf install -y tree
sudo dnf history list tree
sudo dnf history info last
sudo dnf history undo last
rpm -q treeThe last command tells you whether the undo really removed the package. Then find something with more than one version available, using sudo dnf list --showduplicates chrony or the same command on another package, downgrade it, and undo that too. Ten minutes of this on a spare VPS is worth more than any checklist, because the next time you do it the service will be down and the clock will be running.
FAQ
How do I see what the last dnf update changed?
sudo dnf history info last describes the most recent transaction, and sudo dnf history list prints the numbered list so you can pick an older one. To follow a single package instead, run sudo dnf history list httpd. The lower level record lives in /var/log/dnf.log and /var/log/dnf.rpm.log, which is where to look when the run happened overnight and you want the times.
What is the difference between dnf history undo and dnf history rollback?
undo reverses one transaction and leaves everything after it in place. rollback reverses every transaction after the ID you name, so the package set matches the moment that transaction finished. Use undo when you know which run broke the system. Use rollback when several runs happened since the last known good state and you cannot tell them apart.
Why does dnf history undo fail after a point release?
Because an undo has to download the old packages again and dnf keeps no copy of them. The base repositories serve the current minor version only, so once 9.5 becomes 9.6 the previous builds have moved to the archive vault. Check with sudo dnf list --showduplicates and the package name. If the old version is not listed, add the vault as a disabled repository, download the package from it with dnf download, and pass the resulting file to sudo dnf downgrade.
Can I roll back a kernel update on Rocky or AlmaLinux?
Yes, and it is the easiest case, because Enterprise Linux 9 keeps several kernels installed rather than replacing them. rpm -q kernel lists what is on disk, sudo grubby --info=ALL lists the boot entries, and sudo grubby --set-default=/boot/vmlinuz-<version> chooses the one to boot next. Set the default before you reboot, since picking an entry from the boot menu needs the provider's console rather than SSH.
How do I stop the same broken update installing again tonight?
Pin the package. Install python3-dnf-plugin-versionlock, then run sudo dnf versionlock add with the package name, which records the installed version in /etc/dnf/plugins/versionlock.list. Without that pin, the version you just removed is still the newest one in the repository, so the next upgrade run brings it straight back. Write the date and the reason into the lock file, because the pin also blocks security updates for that package.