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
--oldinstallonlyhelp text verified on Fedora 44 (DNF5 5.4.2.1).
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:
uname -rSample output:
6.12.0-211.16.1.el10_2.0.1.x86_64Next, see which kernel GRUB will use after a reboot:
grubby --default-kernelSample output:
/boot/vmlinuz-6.12.0-211.16.1.el10_2.0.1.x86_64On 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:
rpm -q kernel kernel-core | sortSample 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_64If /boot has been filling up, check free space before and after cleanup:
df -h /bootSample output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 920M 170M 687M 20% /bootScroll through the boot menu entries — your distro may also list a rescue image:
grubby --info=ALLSample 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:
grep installonly_limit /etc/dnf/dnf.confSample output:
installonly_limit=3On 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:
sudo dnf autoremove --assumenoSample 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:
rpm -q kernel kernel-core kernel-modules kernel-modules-core | sortSample 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_64After a second kernel lands on the system, the metapackage list grows:
rpm -q kernel | sortSample output:
kernel-6.12.0-211.16.1.el10_2.0.1.x86_64
kernel-6.12.0-211.28.1.el10_2.x86_64Identify old install-only packages
Ask DNF which install-only packages are on the system:
dnf repoquery --installonly --qf '%{name}-%{evr}.%{arch}' | sort -uSample 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_64Add 'kernel*' only when you deliberately want to narrow the list:
dnf repoquery --installonly --qf '%{name}-%{evr}.%{arch}' 'kernel*' | sort -uListing 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.
dnf remove --help | grep -i oldinstallonlySample output:
[--forcearch ARCH] [--duplicates | --oldinstallonly]
--oldinstallonly remove installonly packages over the limitRemove 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.
sudo dnf remove --oldinstallonlyIf you only have two kernel versions installed and installonly_limit is 2, nothing is over the limit yet:
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:
sudo dnf remove --oldinstallonly --setopt installonly_limit=2On 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:
sudo dnf remove --oldinstallonly --setopt installonly_limit=2 'kernel*'Sample 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.
dnf remove --help | grep -i oldinstallonlySample 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:
sudo dnf remove --oldinstallonly --limit=2The 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:
sudo dnf remove --assumeno kernel-core-6.12.0-211.28.1.el10_2.x86_64Sample 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.When the preview looks right, run the same command without --assumeno and confirm:
sudo dnf remove kernel-core-6.12.0-211.28.1.el10_2.x86_64Sample 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:
sudo dnf remove --assumeno kernel-core-6.12.0-211.16.1.el10_2.0.1.x86_64Sample 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:
dnf config-manager --dump | grep protect_running_kernelSample output:
protect_running_kernel = 1On minimal installs without dnf-plugins-core, check the config files directly:
grep -R '^[[:space:]]*protect_running_kernel' /etc/dnf/dnf.conf /etc/dnf/dnf.conf.d/ 2>/dev/nullWhen 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.
sudo yum install yum-utilssudo package-cleanup --oldkernels --count=2To control how many kernels future installs retain, set this in /etc/yum.conf:
[main]
installonly_limit=3To drop one specific build:
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:
package-cleanup --oldkernels --count=2Sample output:
bash: package-cleanup: command not foundThat 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.
uname -rSample output:
6.12.0-211.16.1.el10_2.0.1.x86_64rpm -q kernel kernel-core | sortSample 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_64df -h /bootSample output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 920M 170M 687M 20% /bootRemoving 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:
grubby --info=ALLOn 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.
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 -rbefore every cleanup session. - Check
grubby --default-kernelseparately — 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 removebeforerpm -eunless 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
- DNF configuration reference (
installonly_limit) - Red Hat
dnf.confmanual page - GRUB2 and kernel management on RHEL 8

