Server room with multiple computer towers and networking cables in organized setup
Kernel Modules
William  

Remove Old Kernels on Rocky Linux: Dnf Method

Set installonly_limit to 3 in /etc/dnf/dnf.conf, then run dnf remove --oldinstallonly. That pair is the permanent fix for the "rocky linux remove old kernels" problem, not a one-off cleanup you repeat every month. Skip the limit and you are back here filling /boot again. Skip grub2-mkconfig afterward and the boot menu still lists kernels whose packages are already gone. By the end you'll free space, trim old kernels, and set a simple routine so the operating system stays tidy and predictable at reboot.

Last updated: 2026-07-21

Rocky Linux is not Debian. Forget apt autoremove, dpkg --list, and linux-image package names. On Rocky the tool is dnf, the kernels are RPM packages, and the boot menu is driven by the Boot Loader Specification, not a hand-edited grub.cfg. Get those three facts straight and the rest is short.

Check which kernels are installed before you delete anything

Run these two first, side by side:

uname -r
rpm -q kernel

uname -r prints the kernel you are running right now. rpm -q kernel lists every kernel RPM installed, oldest to newest. The one from uname -r must appear in that list, and you never target it. That is your rollback if the next boot goes sideways.

If you want the whole family, not just the main package, ask for all of them:

rpm -q kernel kernel-core kernel-modules

Each installed kernel usually pulls in kernel-core and kernel-modules too. dnf handles those together when you remove a version, so you do not delete them one by one. Still, seeing them confirms nothing half-installed is lying around.

How do you remove old kernels on Rocky Linux with dnf?

Run the official command:

sudo dnf remove --oldinstallonly

Here is what happens. --oldinstallonly filters to packages marked installonly (kernels are the main one) and removes the oldest ones beyond whatever installonly_limit allows. It will not touch the running kernel and it will not touch the newest one. That is the whole point of the flag: it refuses to leave you unbootable.

Do it in order:

  1. Confirm the running kernel with uname -r.
  2. List what is installed with rpm -q kernel.
  3. Run sudo dnf remove --oldinstallonly.
  4. Read the transaction summary before you type y. Check that the running version is not in the removal list.

If dnf says there is nothing to remove, you are already at or under the limit. That is not an error. It means the automatic cleanup already did its job, which is exactly what the next section sets up.

Stop old kernels from piling up with installonly_limit

dnf remove --oldinstallonly cleans today's mess. installonly_limit stops it coming back. Set it once and every future update prunes for you.

Open /etc/dnf/dnf.conf and set the value:

installonly_limit=3

The number is the total kernels kept, and it counts the one you are running. Rocky ships with a default of 3, which means the running kernel plus the two before it. I keep that default. Dropping to 2 leaves you exactly one fallback, so if a bad update and a flaky older kernel collide, you have no third option to boot into. On a /boot partition that Rocky sizes at about 1 GB by default, three kernels fit fine, so there is no reason to squeeze it to two.

One catch people miss: this setting only governs future installs. Editing dnf.conf does not retroactively delete anything. That is why you run the removal command first, then set the limit. For the full reasoning on why automatic tools leave kernels around, see why autoremove leaves old kernels behind. The dnf behavior is documented in the DNF configuration reference.

Why does the boot menu still show removed kernels?

Because dnf remove does not always regenerate the GRUB config. The package is gone, its vmlinuz may be gone, but the menu entry survives and points at a kernel that no longer exists. Select it and the boot fails.

Rebuild the config yourself. On a BIOS system:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

On UEFI, the file lives on the EFI partition:

sudo grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg

Rocky uses Boot Loader Specification (BLS) entries, one small file per kernel under /boot/loader/entries/. List them:

ls /boot/loader/entries/

Each .conf file there should match a kernel you still have installed. A stale one references a version rpm -q kernel no longer lists. That is your ghost menu entry. After grub2-mkconfig runs, check the directory again and confirm the orphan is gone. If it lingers, delete the specific .conf file by hand and regenerate the config once more.

Clean up what's left behind in /boot

An incomplete removal can leave loose files even when the RPM is uninstalled. Look for three per kernel: vmlinuz-<version>, initramfs-<version>.img, and System.map-<version>.

Measure /boot before and after so you know the cleanup actually did something:

df -h /boot
ls -lh /boot

Compare the vmlinuz and initramfs files listed against rpm -q kernel. Anything in /boot whose version does not appear in the RPM list is a leftover. Removing a single old kernel frees up space, which matters on a 1 GB partition that fills fast.

Do not just rm the files and walk away. If the matching BLS entry still exists, GRUB will try to boot a kernel whose image you just deleted. Remove the leftover file, remove its /boot/loader/entries/ config, then run grub2-mkconfig again. Delete all three, not just the big initramfs.

Why VM clones and templates collect orphaned kernel entries

Clone a VM in the middle of a kernel transaction and you capture a half-finished state. The BLS entry got written but the package database or the /boot image did not finish, or the reverse. The clone boots fine on the old kernel while carrying a menu entry for one that was never fully installed or removed.

There is a second, sharper trap: the machine ID. BLS filenames are keyed to /etc/machine-id. When you build a template and clone it without clearing that ID, every clone shares one identity, and their /boot/loader/entries/ files can collide or reference the wrong build.

The fix on a golden template, before you clone:

sudo truncate -s 0 /etc/machine-id

That forces each clone to generate its own ID on first boot. To find orphans on a clone that is already running, compare ls /boot/loader/entries/ against rpm -q kernel, delete the entries with no matching package, and regenerate GRUB. Snapshot between kernel transactions, never in the middle of one. A snapshot taken mid-dnf is a support ticket waiting to fire.

FAQ

Does removing an old kernel break DKMS or third-party drivers?

Only for the kernel you removed. DKMS builds a module per kernel version, so deleting an old kernel also drops its built modules, which is what you want. The running kernel keeps its modules untouched. After the cleanup, run dkms status to confirm your active kernel still shows its modules built and installed.

Can I remove a specific kernel version instead of the oldest?

Yes. Name it exactly: sudo dnf remove kernel-<version> kernel-core-<version> kernel-modules-<version>. Use the version string straight from rpm -q kernel. dnf still refuses to remove the running kernel, so if you fat-finger the version to match uname -r, it stops you. Rebuild GRUB afterward the same way.

Is dnf-automatic safe to let handle this?

For pruning old kernels, yes, because installonly_limit governs it and it never deletes the running one. What I would not automate is unattended kernel installs plus reboots on a box you cannot afford to have go dark. Auto-clean the old, but keep the reboot into a new kernel a decision you make while watching. If a new kernel misbehaves, rolling back a kernel update is far easier when a known-good version is still installed.

What if dnf says the kernel is protected and refuses to remove it?

That is protect_running_kernel doing its job, and you should leave it on. The message means you asked to remove the version in uname -r. Reboot into a newer kernel first, confirm it is stable, then remove the old one. Never override the protection to delete a kernel you are actively running.

Related on this blog