How to Safely Remove Old Kernels Using DNF or YUM

Learn how to safely remove old Linux kernels using DNF or YUM on RHEL, Rocky Linux, AlmaLinux, Fedora, CentOS Stream, and Oracle Linux while keeping the running kernel and a working fallback.

Published

Updated

Read time 14 min read

Reviewed byDeepak Prasad

Safely remove old Linux kernels using DNF or YUM on RPM-based distributions

When you install a kernel update on an RPM-based system, the old one usually stays in /boot on purpose. That gives you something to boot if the newest build misbehaves. DNF or YUM will often drop versions beyond installonly_limit during a later kernel install, but you may still need a manual pass when /boot is tight, retention is set high, or a half-finished update left extra packages behind.

This guide walks through safe cleanup on RHEL, Rocky Linux, AlmaLinux, CentOS Stream, Fedora, Oracle Linux, and older CentOS or RHEL releases still on YUM. Pick the workflow that matches your host — they are not interchangeable:

Workflow Typical systems Preferred command
DNF4 RHEL 8/9/10, Rocky Linux, AlmaLinux, Oracle Linux dnf remove --oldinstallonly
DNF5 Fedora 41 and later; optional on some enterprise builds dnf remove --oldinstallonly (check dnf remove --help first)
Legacy YUM CentOS 7, RHEL 7, and other YUM-only hosts package-cleanup --oldkernels --count=N

I ran the DNF4 examples on Rocky Linux 10.2 and checked DNF5 flag support in a Fedora 44 container. Repository names and package versions will differ on your machine, but the safety checks and cleanup logic carry over.

Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; DNF 4.20.0. DNF5 --oldinstallonly help text verified on Fedora 44 (DNF5 5.4.2.1).

IMPORTANT
This article covers removing old distribution kernels with DNF or YUM. It does not cover compiling custom kernels, full GRUB administration, or enterprise patch-management policy. For kernel updates before cleanup, see kernel-core update practices and change the default boot kernel.

Before Removing Old Kernels

Take a minute to see what is running, what will boot next, and what you can afford to lose. The kernel in memory and the kernel GRUB picks on reboot are not always the same — especially right after an update you have not rebooted into yet.

Check Command Why it matters
Running kernel uname -r Must not be removed
Default boot kernel grubby --default-kernel May differ from the running kernel
All boot entries grubby --info=ALL Confirms fallback menu entries
Installed kernels rpm -q kernel kernel-core Shows package versions on disk
/boot usage df -h /boot Confirms whether cleanup is necessary
Retention limit grep installonly_limit /etc/dnf/dnf.conf Shows configured keep count

Start with the kernel you are running right now:

bash
uname -r

Sample output:

output
6.12.0-211.16.1.el10_2.0.1.x86_64

Next, see which kernel GRUB will use after a reboot:

bash
grubby --default-kernel

Sample output:

output
/boot/vmlinuz-6.12.0-211.16.1.el10_2.0.1.x86_64

On my test host those matched. After a kernel update without a reboot, it is common for uname -r to still show the older version while grubby --default-kernel already points at the newer vmlinuz.

See which kernel packages are installed:

bash
rpm -q kernel kernel-core | sort

Sample output:

output
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64

If /boot has been filling up, check free space before and after cleanup:

bash
df -h /boot

Sample output:

output
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       920M  170M  687M  20% /boot

Scroll through the boot menu entries — your distro may also list a rescue image:

bash
grubby --info=ALL

Sample output:

output
index=0
kernel="/boot/vmlinuz-6.12.0-211.16.1.el10_2.0.1.x86_64"
args="ro resume=UUID=26765c90-fea0-4216-9293-9936cabf9135 rd.lvm.lv=rlm/root"
root="/dev/mapper/rlm/root"
initrd="/boot/initramfs-6.12.0-211.16.1.el10_2.0.1.x86_64.img"
title="Rocky Linux (6.12.0-211.16.1.el10_2.0.1.x86_64) 10.2 (Red Quartz)"
id="28a80846932844369fe693bfcdfb81bb-6.12.0-211.16.1.el10_2.0.1.x86_64"
index=1
kernel="/boot/vmlinuz-0-rescue-28a80846932844369fe693bfcdfb81bb"
args="ro resume=UUID=26765c90-fea0-4216-9293-9936cabf9135 rd.lvm.lv=rlm/root"
root="/dev/mapper/rlm/root"
initrd="/boot/initramfs-0-rescue-28a80846932844369fe693bfcdfb81bb.img"
title="Rocky Linux (0-rescue-28a80846932844369fe693bfcdfb81bb) 10.2 (Red Quartz)"
id="28a80846932844369fe693bfcdfb81bb-0-rescue"

The rescue entry is not a substitute for a normal fallback kernel. Keep at least one regular kernel version you have actually booted and verified, on top of whatever you are running today.

On production boxes, reboot into a new kernel and make sure it works before you delete older builds you still trust. To change the default entry, see boot an older kernel with grubby.


Understand Kernel Packages and installonly_limit

Kernel RPMs are install-only packages. Installing a new one does not replace the old RPM — both sit on disk until you remove one. DNF and YUM know which names count through installonlypkgs; kernels are in that list by default.

installonly_limit tells the package manager how many install-only versions to keep during future installs or upgrades. Lowering the number in config does not instantly delete extras already sitting in /boot.

Value Practical meaning
3 (common default) Keeps up to three install-only versions
2 Current or newest kernel plus one fallback in the normal case
Higher More rollback flexibility, more /boot consumption
0 Unlimited install-only versions (per man dnf.conf)
1 Rejected as a persistent value on current DNF4 (value 1 is not allowed)

See what your host is configured to keep:

bash
grep installonly_limit /etc/dnf/dnf.conf

Sample output:

output
installonly_limit=3

On legacy YUM systems, look in /etc/yum.conf under [main] for the same setting.

dnf autoremove is not the right tool for planned kernel cleanup. Upstream DNF4 and DNF5 usually leave install-only packages alone, but vendor builds can behave differently — newer RHEL 9 updates, for example, may still list them based on how they were installed. Read the transaction before you confirm anything.

Preview autoremove first:

bash
sudo dnf autoremove --assumeno

Sample output:

output
Last metadata expiration check: 2:24:05 ago on Fri 10 Jul 2026 08:08:51 PM IST.
Dependencies resolved.
Nothing to do.
Complete!

When you specifically want to trim old kernels to match your retention setting, use dnf remove --oldinstallonly.

Changing installonly_limit in a config file alone will not sweep up kernels already installed. Run dnf remove --oldinstallonly when you want to bring the system in line with the limit.

On current RHEL-family releases, the kernel package is often just a metapackage. The real files live in kernel-core, kernel-modules, and kernel-modules-core (sometimes kernel-modules-extra too). Let DNF remove the whole set in one transaction instead of picking files out of /boot by hand.


List Installed and Removable Kernels

List all installed kernel packages

Check the metapackage and the packages that actually carry files:

bash
rpm -q kernel kernel-core kernel-modules kernel-modules-core | sort

Sample output:

output
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-modules-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-modules-core-6.12.0-211.16.1.el10_2.0.1.x86_64

After a second kernel lands on the system, the metapackage list grows:

bash
rpm -q kernel | sort

Sample output:

output
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-6.12.0-211.28.1.el10_2.x86_64

Identify old install-only packages

Ask DNF which install-only packages are on the system:

bash
dnf repoquery --installonly --qf '%{name}-%{evr}.%{arch}' | sort -u

Sample output:

output
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-6.12.0-211.28.1.el10_2.x86_64
kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-core-6.12.0-211.28.1.el10_2.x86_64
kernel-modules-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-modules-6.12.0-211.28.1.el10_2.x86_64
kernel-modules-core-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-modules-core-6.12.0-211.28.1.el10_2.x86_64
kernel-modules-extra-6.12.0-211.16.1.el10_2.0.1.x86_64

Add 'kernel*' only when you deliberately want to narrow the list:

bash
dnf repoquery --installonly --qf '%{name}-%{evr}.%{arch}' 'kernel*' | sort -u

Listing everything install-only is usually safer before cleanup — vendors can add related package names you did not expect. Cross-check against uname -r and grubby --default-kernel before you confirm removal. Third-party kernels (ELRepo, for example) may not get picked up by the default cleanup path, so handle those yourself.


Remove Old Kernels with DNF

Both DNF4 on enterprise hosts and DNF5 on Fedora expose the same cleanup hook: dnf remove --oldinstallonly. On your machine, make sure the flag exists before you bake it into a script.

bash
dnf remove --help | grep -i oldinstallonly

Sample output:

output
[--forcearch ARCH] [--duplicates | --oldinstallonly]
  --oldinstallonly      remove installonly packages over the limit

Remove excess kernels with DNF4

This trims install-only packages above installonly_limit in /etc/dnf/dnf.conf. It skips the kernel you are running. Leave off -y until you have read the transaction list.

bash
sudo dnf remove --oldinstallonly

If you only have two kernel versions installed and installonly_limit is 2, nothing is over the limit yet:

output
No old installonly packages found for removal.
Dependencies resolved.
Nothing to do.
Complete!

When more versions are installed than the limit allows, DNF offers to remove the oldest ones and keep the newest N. Your running kernel stays protected even if it is older than the newest builds on disk — DNF will not boot you out from under yourself. You may end up with more kernel packages than the limit suggests until you reboot or remove a specific version.

To keep only two versions for this one run without editing dnf.conf:

bash
sudo dnf remove --oldinstallonly --setopt installonly_limit=2

On my host, with exactly two kernels already installed, that override still reported nothing to remove.

Some distributions let you scope the cleanup to kernel packages:

bash
sudo dnf remove --oldinstallonly --setopt installonly_limit=2 'kernel*'

Sample output:

output
No old installonly packages found for removal.
Dependencies resolved.
Nothing to do.
Complete!

Try the glob on your release before you depend on it in automation.

Remove excess kernels with DNF5

On Fedora 41 and later, dnf is DNF5. The cleanup idea is the same — dnf remove --oldinstallonly — and DNF5 will not remove the kernel you are running right now.

bash
dnf remove --help | grep -i oldinstallonly

Sample output:

output
--oldinstallonly                   Remove old installonly packages
  --limit=LIMIT                      Limit the number of installonly package versions to keep (must be >=1, used with --oldinstallonly)

A handful of early Fedora 41 builds did not ship --oldinstallonly yet. If you are scripting this, run dnf remove --help on the target host first.

For a one-off cleanup on DNF5, you can cap how many versions to keep with --limit instead of touching dnf.conf:

bash
sudo dnf remove --oldinstallonly --limit=2

The persistent installonly_limit in config cannot go below 2, but DNF5's per-transaction --limit accepts 1 or higher. Keeping only one version leaves you without a normal fallback, so stick with 2 or more on systems where rollback matters.

Setting Minimum
Persistent installonly_limit 2
DNF5 cleanup --limit 1

Out of the box, the config value defaults to 3, rejects 1, and treats 0 as unlimited. For other DNF4 versus DNF5 differences, see DNF4 vs DNF5.

Remove one specific kernel version

Sometimes automatic cleanup is not what you want — say you need to drop one bad build but keep the rest.

Preview removal of a kernel you are not running by targeting kernel-core; DNF should pull the related modules along with it:

bash
sudo dnf remove --assumeno kernel-core-6.12.0-211.28.1.el10_2.x86_64

Sample output:

output
Dependencies resolved.
================================================================================
Package             Arch   Version                  Repository            Size
================================================================================
Removing:
kernel-core         x86_64 6.12.0-211.28.1.el10_2   @ansible-demo-custom  70 M
Removing dependent packages:
kernel              x86_64 6.12.0-211.28.1.el10_2   @ansible-demo-custom   0  
kernel-modules      x86_64 6.12.0-211.28.1.el10_2   @ansible-demo-custom  39 M
kernel-modules-core x86_64 6.12.0-211.28.1.el10_2   @ansible-demo-custom  28 M

Transaction Summary
================================================================================
Remove  4 Packages

Freed space: 136 M
Operation aborted.
NOTE
Look past the core kernel packages in that list. If you run NVIDIA drivers, DKMS, ELRepo kmods, or other out-of-tree modules, the transaction may remove or rebuild related packages too.

When the preview looks right, run the same command without --assumeno and confirm:

bash
sudo dnf remove kernel-core-6.12.0-211.28.1.el10_2.x86_64

Sample output:

output
Removed:
  kernel-6.12.0-211.28.1.el10_2.x86_64
  kernel-core-6.12.0-211.28.1.el10_2.x86_64
  kernel-modules-6.12.0-211.28.1.el10_2.x86_64
  kernel-modules-core-6.12.0-211.28.1.el10_2.x86_64

Complete!

For day-to-day cleanup, prefer dnf remove over rpm -e. DNF lines up the metapackage, modules, and core package together. Removing only the kernel metapackage can show 0 bytes in the summary while the boot files are still there.

Protected running-kernel errors

Try to remove the kernel you are running and DNF4 pushes back:

bash
sudo dnf remove --assumeno kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64

Sample output:

output
Error: 
Problem: The operation would result in removing the following protected packages: kernel-core
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)

That is what you want — a hard stop rather than a broken system. Reboot into the kernel you plan to keep, or pick a different version to remove.

If dnf config-manager is available, you can see whether running-kernel protection is on:

bash
dnf config-manager --dump | grep protect_running_kernel

Sample output:

output
protect_running_kernel = 1

On minimal installs without dnf-plugins-core, check the config files directly:

bash
grep -R '^[[:space:]]*protect_running_kernel' /etc/dnf/dnf.conf /etc/dnf/dnf.conf.d/ 2>/dev/null

When the setting is not spelled out anywhere, upstream DNF4 defaults protect_running_kernel to True.


Remove Old Kernels with Legacy YUM

CentOS 7, RHEL 7, and similar hosts still on YUM use package-cleanup from yum-utils instead of dnf remove --oldinstallonly.

bash
sudo yum install yum-utils
bash
sudo package-cleanup --oldkernels --count=2

To control how many kernels future installs retain, set this in /etc/yum.conf:

ini
[main]
installonly_limit=3

To drop one specific build:

bash
sudo yum remove kernel-<version>

On a modern DNF-only host, package-cleanup may be missing entirely. On Rocky Linux 10.2 it was not installed:

bash
package-cleanup --oldkernels --count=2

Sample output:

output
bash: package-cleanup: command not found

That is normal. Use dnf remove --oldinstallonly on DNF systems — do not treat package-cleanup as the current approach.


Verify the System After Removal

After cleanup, double-check that your running kernel is still there and GRUB still points at a valid vmlinuz.

bash
uname -r

Sample output:

output
6.12.0-211.16.1.el10_2.0.1.x86_64
bash
rpm -q kernel kernel-core | sort

Sample output:

output
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64
bash
df -h /boot

Sample output:

output
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       920M  170M  687M  20% /boot

Removing the non-running 6.12.0-211.28.1 kernel on my test host freed about 63 MiB in /boot (233 MiB used down to 170 MiB).

Removing the RPM usually updates the boot entry too. Confirm with:

bash
grubby --info=ALL

On BLS-based systems (RHEL 8 and later), peek at /boot/loader/entries/ if a removed kernel still shows up in the menu. A reboot does not magically delete a stale entry, and you should not run grub2-mkconfig or delete BLS files by hand unless your distro's recovery docs tell you to.

Reboot into a new kernel and kick the tires before you delete older builds that still work. On important systems, lean on snapshots, rescue media, or whatever recovery path you already trust.


Fix Common Kernel Cleanup Problems

Symptom Likely cause Fix
/boot is full and DNF cannot complete a kernel update Too many kernel RPMs or large initramfs images Remove excess kernels with dnf remove --oldinstallonly; do not delete /boot files manually
--oldinstallonly is not recognized Older DNF5 build or unexpected DNF generation Run dnf remove --help; upgrade DNF or use version-specific removal
Running kernel appears in a proposed removal Running-kernel protection may be disabled, the command is not using normal DNF protection, or package identification is incorrect Stop the transaction; verify uname -r, protect_running_kernel, and the exact package NEVRA
Protected package / kernel-core error Attempt to remove the running kernel Reboot into the kernel to keep, or choose a different version
kernel and kernel-core versions look mismatched Partial manual removal or interrupted transaction List all kernel* RPMs and let DNF remove the full set together
Stale files remain in /boot Files left after non-RPM deletion or broken transaction Remove the owning RPM packages; avoid manual deletion unless support instructs it
GRUB still lists a removed kernel A stale boot entry or incomplete package-removal scriptlet remains Check grubby --info=ALL; on BLS-based systems inspect /boot/loader/entries/, otherwise follow the bootloader procedure for that distribution
Old kernels remain after dnf autoremove autoremove follows dependency reasons rather than the kernel retention limit Use dnf remove --oldinstallonly or version-specific removal
Lowering installonly_limit did nothing Limit affects future installs, not existing packages Run dnf remove --oldinstallonly separately
Third-party kernels not removed Not selected by default install-only cleanup Inspect and remove ELRepo or other vendor packages explicitly
installonly_limit=1 rejected Below the supported minimum on DNF4 Use 2 or higher; see man dnf.conf
No old installonly packages found Installed count is already within the effective retention limit No cleanup is required; verify the count and installonly_limit if the result is unexpected

Automate Kernel Retention Safely

Set installonly_limit in /etc/dnf/dnf.conf or /etc/yum.conf so future kernel installs do not pile up forever.

ini
installonly_limit=3
Setting Effect
2 Keeps the newest install-only versions up to two — one fallback in the common case
3 More conservative; extra rollback option
Too low Risky if a new kernel fails to boot
Changed on disk only Does not by itself remove kernels already installed

Skip cron jobs that grep rpm -q kernel and call rpm -e. The package manager already understands install-only rules and running-kernel protection — ad hoc scripts usually miss a dependency or two.


Best Practices

  • Run uname -r before every cleanup session.
  • Check grubby --default-kernel separately — it is not the same as what is running in memory.
  • Keep at least one tested fallback kernel beyond the rescue entry.
  • Read the transaction list before you pass -y.
  • Remove kernel RPMs with DNF or YUM, not by deleting files under /boot.
  • Reach for dnf remove before rpm -e unless you know every related package name.
  • Boot into and validate a new kernel before you delete older working versions.
  • On critical systems, have snapshots or rescue media ready before aggressive cleanup.

For command references, see the DNF cheat sheet, YUM cheat sheet, and RPM cheat sheet.


Summary

Kernel cleanup comes down to which package manager generation you have. DNF4 and current DNF5 use dnf remove --oldinstallonly; legacy YUM hosts use package-cleanup --oldkernels. Before you remove anything, know what kernel is running, what GRUB will boot next, and that you still have a fallback you trust. installonly_limit shapes future retention — run an explicit cleanup when you need space in /boot today. And treat dnf autoremove as a separate tool: preview it, but use --oldinstallonly when kernel trimming is the goal.


References


Frequently Asked Questions

1. Does dnf autoremove delete old kernels?

Do not use dnf autoremove as your kernel-cleanup command. Upstream DNF4 and DNF5 usually skip install-only packages, but some vendor builds (including RHEL 9.5 and later) may still propose them based on installation reason — always read the transaction. For deliberate kernel trimming, use dnf remove --oldinstallonly.

2. What is the safest way to remove old kernels on Rocky Linux or RHEL?

Check uname -r and grubby --default-kernel, make sure you still have a fallback kernel you trust, then run dnf remove --oldinstallonly without -y so you can review what DNF plans to remove before saying yes.

3. Why does DNF refuse to remove kernel-core?

DNF blocks removal of the kernel you are running. If you target kernel-core for the active version, you get a protected-package error. Remove a different build, or reboot into the kernel you want to keep first.

4. Does installonly_limit immediately delete old kernels?

No. installonly_limit only controls how many install-only versions DNF keeps during future installs or upgrades. To clean up kernels already on disk, run dnf remove --oldinstallonly or remove a specific version.

5. Can I still use package-cleanup --oldkernels on DNF systems?

package-cleanup --oldkernels is part of the old YUM workflow (yum-utils). On modern DNF hosts, use dnf remove --oldinstallonly instead. package-cleanup may not even be installed anymore.

6. Does DNF5 support remove --oldinstallonly?

Yes on current DNF5 builds — and the running kernel stays protected. A few early Fedora 41 builds lacked the flag, so run dnf remove --help on your host before you script around it.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …