Cleaning Up Unused Linux Kernels
After installing another Linux kernel version, the old one usually stays installed. This is not a bug. It is a useful rollback path when the new kernel fails to boot, lacks a driver, or behaves strangely.
Once the system has been rebooted and confirmed healthy, though, keeping too many old kernels becomes a quiet kind of clutter. /boot gets noisy, GRUB entries pile up, and small boot partitions can eventually complain.
This is the checklist I would want before touching a real machine: confirm what is running, remove only the obsolete package set, refresh the boot menu, and leave a fallback kernel unless the host is truly disposable.
Run these commands as root, and do not remove the kernel currently running the system. If the target kernel is shown by uname -r, reboot into another kernel first.
Table of Contents
Confirm the running kernel
Start with the one kernel version that must not be removed.
uname -r
If this prints the version you planned to delete, stop here. Reboot, select a newer or otherwise known-good kernel from GRUB, then check again.
List installed kernel packages
On Red Hat-like systems such as RHEL, CentOS Stream, Rocky Linux, AlmaLinux, or Fedora, query RPM packages:
rpm -aq | grep kernel
On Debian-like systems such as Debian or Ubuntu, query installed linux-* packages:
dpkg -l | grep -Ei 'linux-image|linux-headers|linux-modules' | grep -Ei '^ii' | awk '{ print $2 }'
What you are looking for is the version suffix shared by the packages that belong to the old kernel. For example:
6.8.5-301.fc40.x86_64on a Fedora-style system.5.4.0-165-genericon an Ubuntu-style system.
I usually copy the suffix into a variable and print the matched packages before removing anything. It adds one boring step, and boring is excellent here.
Remove an old kernel on Red Hat-like systems
Use the package manager for the package removal itself, then clean the leftover modules directory if it remains.
deprecated_kernel='6.8.5-301.fc40.x86_64'
if [ "$(uname -r)" = "${deprecated_kernel}" ]; then
echo "Refusing to remove the running kernel: ${deprecated_kernel}" >&2
exit 1
fi
rpm -aq | grep kernel | grep -F "${deprecated_kernel}"
rpm -aq | grep kernel | grep -F "${deprecated_kernel}" | xargs -r dnf remove -y
rm -rf "/lib/modules/${deprecated_kernel}/"
dnf autoremove
If your system only has yum, use it for the final clean-up step:
yum autoremove
Then refresh GRUB configuration files under /boot:
ls -l /boot/ | grep rescue
find /boot/ -name 'grub.cfg' | while IFS= read -r file; do
echo
echo "Updating ${file}:"
grub2-mkconfig -o "${file}"
done
Some systems also keep Boot Loader Specification entries under /boot/loader/entries/. If the removed kernel still has a stale .conf file there, remove that specific file.
ls -l /boot/loader/entries/
Be picky here. Delete only entries that clearly match the kernel version already removed.
Remove an old kernel on Debian-like systems
Again, set the version suffix first. apt purge removes the matching kernel packages and their package configuration; the separate apt autoremove then lets APT clean up dependencies that are no longer needed.
deprecated_kernel='5.4.0-165-generic'
if [ "$(uname -r)" = "${deprecated_kernel}" ]; then
echo "Refusing to remove the running kernel: ${deprecated_kernel}" >&2
exit 1
fi
dpkg -l | grep -Ei 'linux-image|linux-headers|linux-modules' | grep -Ei '^ii' | awk '{ print $2 }' | grep -E "${deprecated_kernel}$"
dpkg -l | grep -Ei 'linux-image|linux-headers|linux-modules' | grep -Ei '^ii' | awk '{ print $2 }' | grep -E "${deprecated_kernel}$" | xargs -r apt purge -y
rm -rf "/lib/modules/${deprecated_kernel}/"
apt autoremove
update-grub
On older Ubuntu releases, this pattern is especially handy after a manual kernel update: headers, image packages, modules, and modules-extra packages may all share the same version suffix.
Do a final sanity check
After clean-up, inspect what remains:
uname -r
# Red Hat-like
rpm -aq | grep kernel
# Debian-like
dpkg -l | grep -Ei 'linux-image|linux-headers|linux-modules' | grep -Ei '^ii' | awk '{ print $2 }'
The result I want is simple:
- The running kernel is still installed.
- At least one known-good fallback kernel remains, unless this is a tightly controlled image-building environment.
- GRUB no longer shows entries for kernel packages that were already removed.
/lib/modules/does not contain a directory for the removed kernel.
Limit future build-up
On Fedora/RHEL-like systems, DNF normally keeps only a small number of install-only packages, including kernels. If the host keeps too many kernels, check the configured limit:
grep -E '^installonly_limit=' /etc/dnf/dnf.conf
For most ordinary hosts, installonly_limit=3 is a reasonable balance: current kernel, one fallback, and one extra slot during updates. I would avoid setting it below 2.
On Debian/Ubuntu, old automatically installed kernels are often handled by:
apt autoremove
If a kernel package was manually marked as installed, APT may keep it. In that case, inspect the package names first and only then adjust the manual/auto mark or purge the exact old packages.
Keep the habit conservative
Kernel clean-up is not about being tidy for its own sake. It is about keeping the boot path understandable while preserving a rollback option.
For normal servers, I prefer keeping the current kernel and one previous known-good kernel. For disposable build hosts or image-making VMs, I may be more aggressive, but only after the new kernel has booted and the important workload has actually run.
