Why we need to clean old kernels
Cleaning old kernels is a vital maintenance task for your Proxmox and RHEL/Ubuntu systems. In Linux, the kernel is the core software that manages your hardware; when you update your system, new versions are installed, but old ones are often left behind.
1. Protect the Boot Partition
Most systems have a dedicated /boot partition that is quite small (often 500MB to 1GB). Every new kernel takes up about 50–100MB. If this fills up to 100%, your system will:
Fail to install future security updates.
Fail to boot if the update process is interrupted by a lack of space.
2. Improve Security & Performance
Reduce Attack Surface: Old kernels can have unpatched vulnerabilities. Removing them ensures an attacker can't force your system to reboot into a buggy, older version.
Faster Booting: It keeps your GRUB boot menu clean, making it easier to select the correct "fallback" version if a new update ever fails.
3. Compliance & Hygiene
If you are following CIS Benchmarks or using OpenSCAP for hardening (as seen in your recent projects), removing unused software—including old kernels—is a standard requirement to keep the OS lean and compliant.
Safe Practice: Always keep at least two kernels: the one you are currently using and the previous stable version as a backup.
Lets run script with output. Its a smart script which will list all installed scripts and protect two kernels, current and previous also will ask for input to protect if any and then proceeed to cleanup old kernels.
# cat purge-old-kernels.sh
#!/bin/bash
# 1. Extraction with Regex to get just the version numbers
RUNNING_RAW=$(uname -r | sed 's/-pve//;s/-signed//')
LATEST_INSTALLED=$(dpkg -l | grep "^ii proxmox-kernel-[0-9]" | awk '{print $2}' | grep -v "helper" | sort -V | tail -n 1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+-[0-9]+")
# 2. Safety Check for Running Kernel
CHECK_RUNNING=$(dpkg -l | grep "ii" | grep "$RUNNING_RAW")
if [ -z "$CHECK_RUNNING" ]; then
echo "!!! CRITICAL ERROR !!!"
echo "Running kernel ($RUNNING_RAW) is NOT in dpkg list."
echo "Run: apt install --reinstall proxmox-kernel-$RUNNING_RAW-pve-signed"
exit 1
fi
# 3. Display Clean, Unique Kernel List
echo "--------------------------------------------------------"
echo "INSTALLED KERNELS (Available for Exceptions):"
echo "--------------------------------------------------------"
# This gets the version, strips the junk, sorts uniquely, and formats into columns
dpkg -l | grep -E "^ii (proxmox|pve)-kernel-[0-9]" | \
awk '{print $2}' | \
sed -E 's/(proxmox|pve)-kernel-//; s/-(signed|pve)//g' | \
sort -uV | column -x
echo "--------------------------------------------------------"
echo "AUTO-PROTECTING:"
echo " - Running: $RUNNING_RAW"
echo " - Latest: $LATEST_INSTALLED"
echo "--------------------------------------------------------"
# 4. User Input
read -p "Enter any EXTRA versions to keep (space separated): " USER_EXTRAS
PROTECT_LIST=("$RUNNING_RAW" "$LATEST_INSTALLED" $USER_EXTRAS)
echo -e "\nFinal Protection List: ${PROTECT_LIST[*]}"
read -p "Proceed with purging all other kernels? (y/N): " CONFIRM
if [[ $CONFIRM != [yY] ]]; then echo "Aborting."; exit 1; fi
# 5. Purge Loop
ALL_PKGS=$(dpkg -l | grep -E "(^ii|^rc) (proxmox|pve)-kernel-" | grep -v "helper" | awk '{print $2}')
for pkg in $ALL_PKGS; do
KEEP=false
for prot in "${PROTECT_LIST[@]}"; do
if [[ "$pkg" =~ "$prot" ]]; then
KEEP=true
break
fi
done
if [ "$KEEP" = true ]; then
echo "[KEEPING] $pkg"
else
# Shield the Meta-package for the running major version
CURRENT_MAJOR=$(echo $RUNNING_RAW | cut -d'.' -f1,2)
if [[ "$pkg" == "proxmox-kernel-$CURRENT_MAJOR" ]]; then
echo "[KEEPING] $pkg (Active Meta-package)"
else
echo "[PURGING] $pkg..."
DEBIAN_FRONTEND=noninteractive apt-get purge -y "$pkg" > /dev/null 2>&1
fi
fi
done
# 6. Post-Cleanup
echo -e "\nRefreshing boot configuration..."
proxmox-boot-tool refresh || update-grub
apt-get autoremove -y > /dev/null 2>&1
echo "Done."