769

Every time I install a new Linux kernel, it gets left in the grub_config, making the boot menu longer each time.

I know I can manually search through the installed packages and remove them.

Does Ubuntu provide any easier way to clean them up or keep them from showing in the boot list?

strpeter
  • 146

40 Answers40

652

16.04 and newer versions of Ubuntu

sudo apt autoremove

This command removes packages that were automatically installed to resolve a dependency, but are now no longer depended on. This includes old versions of linux-headers-* and linux-image-*. (It’s also smart about this process, leaving one spare version of the kernel around as a fallback!)

11.10 and newer versions of Ubuntu

GRUB2 and its display of all kernels

The latest versions of Grub2 installed in Ubuntu automatically display the latest kernel and hides the older kernels that you may have installed.

GNU GRUB

If you do not see your grub - then remember to press Shift whilst booting.

As you can see, only the latest kernel is displayed.

If you select the option shown (press Enter) then all the old kernels become visible and available to boot from.

GNU GRUB Previous versions

How to permanently delete older kernels

First boot with the latest available kernel.

There are a number of ways to delete old kernels. Personally, I wouldn't touch Computer Janitor since this is acknowledged to break your computer with its suggestions.

synaptic

An alternative is Synaptic (sudo apt install synaptic)

search for linux-image, right-click a kernel and choose complete removal and finally click the Apply button to delete the kernel.

Synaptic Package Manager

Repeat the search but this time for linux-header - you can delete the associated headers for the kernel image chosen previously.

Synaptic though will not attempt to verify what you are trying to remove... you could inadvertently delete your newest kernel - or even delete all of your kernels via this tool leaving you with an unbootable Ubuntu!.

Remember to check which kernel you are using type:

uname -r

The result would be similar to:

Terminal <uname -r>

Remember the result and the number - make sure you don't delete the corresponding image or header.

Recommendation

My recommendation is to keep at least two or preferably three kernels including the latest. The reason for the recommendation is that you will have at least one/two other kernels to boot with, if for what-ever reason the latest kernel you are unable to boot with or introducing a regressed capability such as broken wireless.

fossfreedom
  • 174,526
428

First, reboot your System to be sure it is using the latest kernel. Then open terminal and check your current kernel:

uname -r 

DO NOT REMOVE THIS KERNEL!

Next, type the command below to view/list all installed kernels on your system.

dpkg --list 'linux-image-*'

Find all the kernels that lower than your current kernel. When you know which kernel to remove, continue below to remove it. Run the commands below to remove the kernel you selected.

sudo apt-get purge linux-image-x.x.x-x-generic 

Finally, run the commands below to update grub2

sudo update-grub2 

Reboot your system.

penreturns
  • 5,950
316

My one-liner to remove old kernels (this also frees up disk space)

dpkg --list | grep linux-image | awk '{ print $2 }' | sort -V | sed -n '/'`uname -r`'/q;p' | xargs sudo apt-get -y purge

Explanation (remember, | uses the output of the previous command as the input to the next)

  • dpkg --list lists all installed packages
  • grep linux-image looks for the installed linux images
  • awk '{ print $2 }' just outputs the 2nd column (which is the package name)
  • sort -V puts the items in order by version number
  • sed -n '/'`uname -r`'/q;p' prints the lines before the current kernel
  • xargs sudo apt-get -y purge purges the found kernels

Unwinding the sed invocation:

  • -n tells sed to be quiet
  • `uname -r` outputs the current installed kernel release - we include it in backticks so that the output is includes as part of the command (you might also see this as $(uname -r)
  • /something/q says stop when you match 'something' (in this case, something is output of uname -r) - the / surround a regular expression
  • p is print
  • the ; is the command separtor, so /something/q;p says quit when you match something, else print

altogether, sed -n '/'`uname -r`'/q;p' is print the lines until it matches with the current kernel name.

If you're paranoid (like me), you can make the last part xargs echo sudo apt-get -y purge so that the command to purge the old kernels is printed, then you can check that nothing unexpected is included before you run it.


Modified version to remove headers:

dpkg --list | grep 'linux-image' | awk '{ print $2 }' | sort -V | sed -n '/'"$(uname -r | sed "s/\([0-9.-]*\)-\([^0-9]\+\)/\1/")"'/q;p' | xargs sudo apt-get -y purge
dpkg --list | grep 'linux-headers' | awk '{ print $2 }' | sort -V | sed -n '/'"$(uname -r | sed "s/\([0-9.-]*\)-\([^0-9]\+\)/\1/")"'/q;p' | xargs sudo apt-get -y purge

Note: the sed invocation is modified. "$(uname -r | sed "s/\([0-9.-]*\)-\([^0-9]\+\)/\1/")" extracts only the version (e.g. "3.2.0-44") , without "-generic" or similar from uname -r


All-in-one version to remove images and headers (combines the two versions above):

echo $(dpkg --list | grep linux-image | awk '{ print $2 }' | sort -V | sed -n '/'`uname -r`'/q;p') $(dpkg --list | grep linux-headers | awk '{ print $2 }' | sort -V | sed -n '/'"$(uname -r | sed "s/\([0-9.-]*\)-\([^0-9]\+\)/\1/")"'/q;p') | xargs sudo apt-get -y purge
chesedo
  • 1,749
  • 1
  • 14
  • 25
65

Ubuntu 16.04+:

$ sudo apt autoremove
...
The following packages will be REMOVED:
  linux-headers-4.4.0-57 linux-headers-4.4.0-57-generic linux-image-4.4.0-57-generic linux-image-extra-4.4.0-57-generic linux-tools-4.4.0-57 linux-tools-4.4.0-57-generic

Ubuntu 15.10 and below:

I find this to be the easiest and quickest way. It keeps the latest kernel as well as two more:

sudo apt-get install bikeshed
sudo purge-old-kernels

To change the number of additional kernels that are kept:

sudo purge-old-kernels --keep 3
bmaupin
  • 5,371
44

Removing Entries from Grub 2 Entries should be removed by editing or removing files in the /etc/grub.d folder. The /boot/grub/grub.cfg file is read-only and should not normally require editing.

Too Many Kernels?

  • If you are not sure of the kernel you are currently using, in a terminal type uname -r.

  • Kernels removed via APT (Synaptic, "apt-get remove", etc.) will automatically update grub.cfg and no user action is required.

  • A great tool for removing kernels (and menu entries) is Ubuntu-Tweak, a safe and easy-to-use GUI app.

  • Install ubuntu tweak

  • Ubuntu-Tweak will be available under Applications > System Tools.

Remove Older Kernel Entries

  • Select "Package Cleaner" on the left and "Clean Kernel" from the right panel.

  • Press the "Unlock" button at the lower right, enter your password.

  • Select from the displayed list the kernel images and headers you wish to remove. The kernel in use is not listed.

  • Press the "Cleanup" button at the lower right to remove the selected kernel images and headers.

Remove Operating Systems from the Grub menu

  • Other Operating Systems which have been removed from the computer will also be removed from the menu once "update-grub" is run as root.

  • Menu items are placed on the Grub2 menu by scripts. If you don't want other Operating Systems to be entered in the menu, disable /etc/grub.d/30_osprober

  • Run this command to stop the script from running
    sudo chmod -x /etc/grub.d/30_os-prober

  • DISABLE_30_OS-PROBER='true' in /etc/default/grub

Remove Memtest86+ from the Grub Menu
sudo chmod -x /etc/grub.d/20_memtest86+

  • Run the update-grub command to allow the changes to be incorporated in grub.cfg

Source

Note: After kernel updates a new entry is added to the GRUB menu.You can remove the older one if you want.However, most experienced users will advise you to keep at least one spare entry in case something goes wrong with an upgrade and you need to boot an older kernel version for troubleshooting purposes.

Alternate way to remove Kernel entries (prior to 10.04)

for GRUB not GRUB2

startupmanager Install startupmanager

You can find it under System>>Administration>> alt text
alt text
You see in the second screenshot you can select how many kernels to show? I generally just keep it on 1, but when I get a kernel upgrade I always change it to 2 before restarting so I can select the older kernel if the new kernel has problems with my hardware. Once I know the new kernel is working well I change it back to 1.

karthick87
  • 84,513
41

Purely commandline, this will remove all but the current and second most current (via the "-2" in the head command below):

OLD=$(ls -tr /boot/vmlinuz-* | head -n -2 | cut -d- -f2- |
    awk '{print "linux-image-" $0 " linux-headers-" $0}' )
if [ -n "$OLD" ]; then
    apt-get -qy remove --purge $OLD
fi
apt-get -qy autoremove --purge
jarno
  • 6,175
Kees Cook
  • 17,823
40

Update: purge-old-kernels is deprecated nowadays.

I made a script to purge kernels even in tricky conditions. It is called linux-purge and you can find it here.

If you just want to purge the kernels (and the related packages) that are older than the currently used kernel, when system is not broken, you could use this script.

There is also an Ubuntu documentation page that I have contributed to concerning removing old kernels here.

jarno
  • 6,175
30

You can follow the Using the "unattended-upgrades" package section of Automatic Security Updates article on Ubuntu Wiki to perform this.

You need to change the following line in /etc/apt/apt.conf.d/50unattended-upgrades file;

//Unattended-Upgrade::Remove-Unused-Dependencies "false";

with

Unattended-Upgrade::Remove-Unused-Dependencies "true";

to automatically remove old packages, including kernels.

Also remove or comment the line

"^linux-image.*"; 

in the "NeverAutoRemove" section of the file /etc/apt/apt.conf.d/01autoremove.

Qwerty
  • 191
28

The fastest/simpler way (Applicable at least since 12.04) possible that already comes with Ubuntu is apt-get. Do the following if you wish to remove all older kernel versions that are not in use (Except the previous one that you are no using. This is to make sure that if the current kernel version fails in some way, you have a way to go back to a previous state). Do the following:

sudo apt-get autoclean

This will eliminate any old files (Including kernel versions) you may have. Note that if you have many old versions, it will take a while since it has to make sure that removing the kernel version has no issues. For me, removing the last 12 kernel versions took about 2 minutes. You can also do the following:

sudo apt-get clean

Which will eliminate everything downloaded and stored in the cache folder of apt. Lastly you have:

sudo apt-get autoremove

which would check for any unused packages and remove them if necessary. This is great for those libraries and dependency packages that are no longer needed byt any app installed.

wjandrea
  • 14,504
Luis Alvarado
  • 216,643
27

To figure out what kernels and headers are installed use

dpkg -l | grep linux-image

dpkg -l | grep linux-headers

You can then remove them one by one or together, just make sure to keep the most recent.

There are also some handy commands and scripts to automate the removal.

http://ubuntuforums.org/showthread.php?t=1658648

The following claims to remove all unused kernels and headers:

dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d'|grep -E "(image|headers|modules)" | grep -v hwe | xargs sudo apt-get purge

(use apt-get -y to remove without question)

Here is what happens when run on 18.04.1:

~$ dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]

\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d'|grep -E "(image|headers|modules)" | xargs sudo apt-get -y purge
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be REMOVED:
  linux-headers-4.15.0-33* linux-headers-4.15.0-33-generic* linux-headers-4.15.0-34* linux-headers-4.15.0-34-generic* linux-image-4.15.0-33-generic* linux-image-4.15.0-34-generic*
  linux-modules-4.15.0-33-generic* linux-modules-4.15.0-34-generic* linux-modules-extra-4.15.0-33-generic* linux-modules-extra-4.15.0-34-generic*
0 upgraded, 0 newly installed, 10 to remove and 1 not upgraded.
After this operation, 671 MB disk space will be freed.
(Reading database ... 227403 files and directories currently installed.)
Removing linux-headers-4.15.0-33-generic (4.15.0-33.36) ...
Removing linux-headers-4.15.0-33 (4.15.0-33.36) ...
Removing linux-headers-4.15.0-34-generic (4.15.0-34.37) ...
Removing linux-headers-4.15.0-34 (4.15.0-34.37) ...
Removing linux-modules-extra-4.15.0-33-generic (4.15.0-33.36) ...
Removing linux-image-4.15.0-33-generic (4.15.0-33.36) ...
/etc/kernel/postrm.d/initramfs-tools:
update-initramfs: Deleting /boot/initrd.img-4.15.0-33-generic
/etc/kernel/postrm.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.0-36-generic
Found initrd image: /boot/initrd.img-4.15.0-36-generic
Found linux image: /boot/vmlinuz-4.15.0-34-generic
Found initrd image: /boot/initrd.img-4.15.0-34-generic
Adding boot menu entry for EFI firmware configuration
done
Removing linux-modules-extra-4.15.0-34-generic (4.15.0-34.37) ...
Removing linux-image-4.15.0-34-generic (4.15.0-34.37) ...
I: /vmlinuz.old is now a symlink to boot/vmlinuz-4.15.0-36-generic
I: /initrd.img.old is now a symlink to boot/initrd.img-4.15.0-36-generic
/etc/kernel/postrm.d/initramfs-tools:
update-initramfs: Deleting /boot/initrd.img-4.15.0-34-generic
/etc/kernel/postrm.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.0-36-generic
Found initrd image: /boot/initrd.img-4.15.0-36-generic
Adding boot menu entry for EFI firmware configuration
done
Removing linux-modules-4.15.0-33-generic (4.15.0-33.36) ...
Removing linux-modules-4.15.0-34-generic (4.15.0-34.37) ...
(Reading database ... 156180 files and directories currently installed.)
Purging configuration files for linux-image-4.15.0-34-generic (4.15.0-34.37) ...
Purging configuration files for linux-modules-4.15.0-33-generic (4.15.0-33.36) ...
dpkg: warning: while removing linux-modules-4.15.0-33-generic, directory '/lib/modules/4.15.0-33-generic' not empty so not removed
Purging configuration files for linux-modules-4.15.0-34-generic (4.15.0-34.37) ...
Purging configuration files for linux-image-4.15.0-33-generic (4.15.0-33.36) ...
Purging configuration files for linux-modules-extra-4.15.0-34-generic (4.15.0-34.37) ...
Purging configuration files for linux-modules-extra-4.15.0-33-generic (4.15.0-33.36) ...
~$ uname -r
4.15.0-36-generic
rubo77
  • 34,024
  • 52
  • 172
  • 299
mikewhatever
  • 33,013
26

10.04 GUI Method

Computer Janitor can clean up old kernels and I believe is installed by default in Ubuntu (but not Kubuntu).

GRUB 1, if you're using that, has an option in /boot/grub/menu.lst to specify how many kernels it should show at a maximum. GRUB 2, as far as I can tell, does not.

maco
  • 16,132
17

In order to remove older Linux image kernels, first boot in the kernel you want to keep.

You can also check the kernel version using command uname -r so that you don't remove the wrong one by mistake.

Now go to synaptic package manager and search for linux-image and remove the older versions except the one shown by upper command. Generally I prefer to go with the latest one.

Now when you restart you'll see a more clean grub menu.

Yi Jiang
  • 1,206
15

You could install ubuntu-tweak and then Go to Applications -> System tool -> ubuntu tweak and

enter image description here click package cleaner and clean kernels. it does not show the currently used kernel so you will always be safe.

Lincity
  • 25,749
9

Personally, I like using Synaptic. It makes me feel more secure about what's going on. The only app I've used that has an option to remove old kernels is Ubuntu Tweak.

How to remove the kernels you are not using:

  • Open UbuntuTweak
  • Click on 'Package Cleaner' under 'Applications' in the left-hand pane
  • On the right side of the 'cleaning view' press 'Clean Kernels'
  • Select all kernels - I think the one in use is not listed but just in case check running uname -a in a terminal
Kevin Bowen
  • 20,055
  • 57
  • 82
  • 84
7

You can uninstall the old kernels (linux-image-... packages) using Synaptic, and that will remove them from the boot menu. Take care not to remove the running kernel (you can check its version with uname -r).

Bear in mind that having a one or two older versions can help you troubleshoot, should something go wrong.

Alternatively, you can edit/remove the entries manually (gksu gedit /boot/grub/grub.cfg), but they will be re-generated when you update to a newer kernel. If you are thinking about removing recovery mode options - don't. They can come in handy if you break something which prevents you from booting.


Refer to this page.

evgeny
  • 9,865
5

This is a pure command line solution.

First generate a list of all installed kernel versions except the currently running kernel:

dpkg-query -W -f='${Package}\n' |
  grep -f <(ls -1 /boot/vmlinuz* | cut -d- -f2,3 |
    grep -v $(uname -r | cut -d- -f1,2))

Alternatively generate a list of all installed kernel versions except the last two:

dpkg-query -W -f='${Package}\n' |
  grep -f <(ls -1 /boot/vmlinuz* | cut -d- -f2,3 |
    sort -V | head -n -2)

Examine the list. Make sure the the kernel versions you want to keep are not part of the list. Use the command uname -r to see the version of the currently running kernel.

If you are happy with the results you can use apt-get to remove the packages.

First a dry run (using the first generator as example):

sudo apt-get --dry-run purge $(
  dpkg-query -W -f='${Package}\n' |
    grep -f <(ls -1 /boot/vmlinuz* | cut -d- -f2,3 |
      grep -v $(uname -r | cut -d- -f1,2)))

Then a real run:

sudo apt-get purge $(
  dpkg-query -W -f='${Package}\n' |
    grep -f <(ls -1 /boot/vmlinuz* | cut -d- -f2,3 |
      grep -v $(uname -r | cut -d- -f1,2)))

If you want to automate the process then add the --yes parameter:

sudo apt-get --yes purge $(
  ...)
Lesmana
  • 18,754
5

The advantage of this answer is native Ubuntu Bash is used without installing third-party applications. Users of custom kernels who didn't use apt or dpkg can change this bash script to suit their needs. This answer is based on (How to selectively purge old kernels all at once).

Zenity based solution

Zenity provides a nice GUI interface to the terminal to process a list and select items with radio-buttons:

rm-kernels 1

As the title indicates the current kernel you booted with cannot be removed and isn't included in the list. The size reported is how much will be saved in /boot directory. More is saved on your disk because kernel binaries reside in other areas too. July 27, 2017 note: The directories /usr/src/*kernel_version* and /lib/modules/*kernel_version* are now included as well.

The Modified Date is discovered using the stat command. On my system that date is "touched" every time the kernel is booted using this (How do you find out when a specific kernel version was last booted?) cron reboot script. However, on your system the date will be the kernel release date, not the last time you booted it.

apt-get purge gives you chance to abort

You are given a final opportunity to view everything that will be purged and see the total disk space (somewhat misleading) that will be recovered:

The following packages will be REMOVED:
  linux-headers-4.7.1-040701* linux-headers-4.7.1-040701-generic*
  linux-headers-4.7.2-040702* linux-headers-4.7.2-040702-generic*
  linux-headers-4.7.3-040703* linux-headers-4.7.3-040703-generic*
  linux-headers-4.8.1-040801* linux-headers-4.8.1-040801-generic*
  linux-headers-4.8.10-040810* linux-headers-4.8.10-040810-generic*
  linux-headers-4.8.11-040811* linux-headers-4.8.11-040811-generic*
  linux-headers-4.8.4-040804* linux-headers-4.8.4-040804-generic*
  linux-headers-4.8.5-040805* linux-headers-4.8.5-040805-generic*
  linux-image-4.7.1-040701-generic* linux-image-4.7.2-040702-generic*
  linux-image-4.7.3-040703-generic* linux-image-4.8.1-040801-generic*
  linux-image-4.8.10-040810-generic* linux-image-4.8.11-040811-generic*
  linux-image-4.8.4-040804-generic* linux-image-4.8.5-040805-generic*
0 upgraded, 0 newly installed, 24 to remove and 2 not upgraded.
After this operation, 2,330 MB disk space will be freed.
Do you want to continue? [Y/n] 

The Code

Copy this code to an executable file named rm-kernels in /usr/local/bin:

#!/bin/bash

# NAME: rm-kernels
# PATH: /usr/local/bin
# DESC: Provide zenity item list of kernels to remove

# DATE: Mar 10, 2017. Modified Jul 28, 2017.

# NOTE: Will not delete current kernel.

#       With 10 kernels on an SSD, empty cache from sudo prompt (#) using:
#       # free && sync && echo 3 > /proc/sys/vm/drop_caches && free
#       First time for `du` 34 seconds.
#       Second time for `du` 1 second.

# PARM: If any parm 1 passed use REAL kernel size, else use estimated size.
#       By default `du` is not used and estimated size is displayed.

# Must be running as sudo
if [[ $(id -u) != 0 ]]; then
    zenity --error --text "root access required. Use: sudo rm-kernels"
    exit 99
fi

OLDIFS="$IFS"
IFS="|"
choices=()

current_version=$(uname -r)

for f in /boot/vmlinuz*
do
    if [[ $f == *"$current_version"* ]]; then continue; fi # skip current version
    [[ $f =~ vmlinuz-(.*) ]]
    v=${BASH_REMATCH[1]}        # example: 4.9.21-040921-generic
    v_main="${v%-*}"            # example: 4.9.21-040921

    # Kernel size in /boot/*4.9.21-040921-generic*
    s=$(du -ch /boot/*-$v* | awk '/total/{print $1}')

    if [[ $# -ne 0 ]] ; then    # Was a parameter passed?
        if [[ -d "/usr/src/linux-headers-"$v_main ]] ; then
             # Kernel headers size in /usr/src/*4.9.21-040921*
             s2=$(du -ch --max-depth=1 /usr/src/*-$v_main* | awk '/total/{print $1}')
        else
             s2="0M"            # Linux Headers are not installed
        fi
        # Kernel image size in /lib/modules/4.9.21-040921-generic*
        s3=$(du -ch --max-depth=1 /lib/modules/$v* | awk '/total/{print $1}')
    else
        # Estimate sizof of optional headers at 125MB and size of image at 220MB
        if [[ -d "/usr/src/linux-headers-"$v_main ]] ; then
             s2="125M"
        else
             s2="0M"            # Linux Headers are not installed
        fi
        s3="220M"
    fi

    # Strip out "M" provided by human readable option of du and add 3 sizes together
    s=$(( ${s//[^0-9]*} + ${s2//[^0-9]*} + ${s3//[^0-9]*} ))
    t=$(( t + s ))
    s=$s" MB"
    d=$(date --date $(stat -c %y $f) '+%b %d %Y') # Last modified date for display
    choices=("${choices[@]}" false "$v" "$d" "$s")
done

# adjust width & height below for your screen 640x480 default for 1920x1080 HD screen
# also adjust font="14" below if blue text is too small or too large

choices=(`zenity \
        --title "rm-kernels - Total: $t MB excluding: $current_version" \
        --list \
        --separator="$IFS" \
        --checklist --multiple \
        --text '<span foreground="blue" font="14">Check box next to kernel(s) to remove</span>' \
        --width=640 \
        --height=480 \
        --column "Select" \
        --column "Kernel Version Number" \
        --column "Modified Date" \
        --column " Size " \
        "${choices[@]}"`)
IFS="$OLDIFS"

i=0
list=""
for choice in "${choices[@]}" ; do
    if [ "$i" -gt 0 ]; then list="$list- "; fi # append "-" from last loop
    ((i++))

    short_choice=$(echo $choice | cut -f1-2 -d"-")
    header_count=$(find /usr/src/linux-headers-$short_choice* -maxdepth 0 -type d | wc -l)

    # If -lowlatency and -generic are purged at same time the _all header directory
    # remains on disk for specific version with no -generic or -lowlatency below.
    if [[ $header_count -lt 3 ]]; then
        # Remove all w.x.y-zzz headers
        list="$list""linux-image-$choice- linux-headers-$short_choice"
    else
        # Remove w.x.y-zzz-flavour header only, ie -generic or -lowlatency
        list="$list""linux-image-$choice- linux-headers-$choice" 
    fi

done

if [ "$i" -gt 0 ] ; then
     apt-get purge $list
fi

NOTE: You need sudo permission to create the file so use:

gksu gedit /usr/local/bin/rm-kernels

To make file executable use:

sudo chmod +x /usr/local/bin/rm-kernels

Server Version

rm-kernels-server is the server version to selectively delete kernels all at once. Instead of a GUI (graphical) dialog box a text-based dialog box is used to select kernels to purge.

  • Before running the script you need to install the dialog function using:

    sudo apt install dialog

Dialog is in the default Ubuntu Desktop installation but not in Ubuntu Server.

Sample screen

rm-kernels-server 1

rm-kernels-server bash code

#!/bin/bash

# NAME: rm-kernels-server
# PATH: /usr/local/bin
# DESC: Provide dialog checklist of kernels to remove
#       Non-GUI, text based interface for server distro's.

# DATE: Mar 10, 2017. Modified Jul 28, 2017.

# NOTE: Will not delete current kernel.

#       With 10 kernels on an SSD, empty cache from sudo prompt (#) using:
#       # free && sync && echo 3 > /proc/sys/vm/drop_caches && free
#       First time for `du` 34 seconds.
#       Second time for `du` 1 second.

# PARM: If any parm 1 passed use REAL kernel size, else use estimated size.
#       By default `du` is not used and estimated size is displayed.

# Must be running as sudo
if [[ $(id -u) != 0 ]]; then
    echo "root access required. Use: sudo rm-kernels-server"
    exit 99
fi

# Must have the dialog package. On Servers, not installed by default
command -v dialog >/dev/null 2>&1 || { echo >&2 "dialog package required but it is not installed.  Aborting."; exit 99; }

OLDIFS="$IFS"
IFS="|"
item_list=() # Deviate from rm-kernels here.

current_version=$(uname -r)
i=0
for f in /boot/vmlinuz*
do
    if [[ $f == *"$current_version"* ]]; then continue; fi # skip current version
    [[ $f =~ vmlinuz-(.*) ]]
    ((i++)) # Item List
    v=${BASH_REMATCH[1]}        # example: 4.9.21-040921-generic
    v_main="${v%-*}"            # example: 4.9.21-040921

    # Kernel size in /boot/*4.9.21-040921-generic*
    s=$(du -ch /boot/*-$v* | awk '/total/{print $1}')

    if [[ $# -ne 0 ]] ; then    # Was a parameter passed?
        if [[ -d "/usr/src/linux-headers-"$v_main ]] ; then
             # Kernel headers size in /usr/src/*4.9.21-040921*
             s2=$(du -ch --max-depth=1 /usr/src/*-$v_main* | awk '/total/{print $1}')
        else
             s2="0M"            # Linux Headers are not installed
        fi
        # Kernel image size in /lib/modules/4.9.21-040921-generic*
        s3=$(du -ch --max-depth=1 /lib/modules/$v* | awk '/total/{print $1}')
    else
        # Estimate sizof of optional headers at 125MB and size of image at 220MB
        if [[ -d "/usr/src/linux-headers-"$v_main ]] ; then
             s2="125M"
        else
             s2="0M"            # Linux Headers are not installed
        fi
        s3="220M"
    fi

    # Strip out "M" provided by human readable option of du and add 3 sizes together
    s=$(( ${s//[^0-9]*} + ${s2//[^0-9]*} + ${s3//[^0-9]*} ))
    t=$(( t + s ))
    s=$s" MB"
    d=$(date --date $(stat -c %y $f) '+%b %d %Y') # Last modified date for display
    item_list=("${item_list[@]}" "$i" "$v ! $d ! $s" off)
done

cmd=(dialog --backtitle "rm-kernels-server - Total: $t MB excluding: $current_version" \
    --title "Use space bar to toggle kernel(s) to remove" \
    --column-separator "!" \
    --separate-output \
    --ascii-lines \
    --checklist "         Kernel Version --------- Modified Date  Size" 20 60 15)

selections=$("${cmd[@]}" "${item_list[@]}" 2>&1 >/dev/tty)

IFS=$OLDIFS

if [ $? -ne 0 ] ; then
    echo cancel selected
    exit 1
fi

i=0
choices=()

for select in $selections ; do
    ((i++))
    j=$(( 1 + ($select - 1) * 3 ))
    choices[i]=$(echo ${item_list[j]} | cut -f1 -d"!")
done

i=0
list=""
for choice in "${choices[@]}" ; do
    if [ "$i" -gt 0 ]; then list="$list- "; fi # append "-" from last loop
    ((i++))

    short_choice=$(echo $choice | cut -f1-2 -d"-")
    header_count=$(find /usr/src/linux-headers-$short_choice* -maxdepth 0 -type d | wc -l)

    # If -lowlatency and -generic are purged at same time the _all header directory
    # remains on disk for specific version with no -generic or -lowlatency below.
    if [[ $header_count -lt 3 ]]; then
        # Remove all w.x.y-zzz headers
        list="$list""linux-image-$choice- linux-headers-$short_choice"
    else
        # Remove w.x.y-zzz-flavour header only, ie -generic or -lowlatency
        list="$list""linux-image-$choice- linux-headers-$choice" 
    fi

done

if [ "$i" -gt 0 ] ; then
    apt-get purge $list
fi

NOTE: In the call to dialog the directive --ascii-lines is passed to replace line-draw extended character set (which ssh doesn't like) with "+-----+" for drawing boxes. If you do not like this appearance you can use the --no-lines directive for no box at all.


July 28, 2017 Updates

The calculated size of each kernel was taken from /boot/*kernel_version* which were 5 files totaling ~50 MB. The formula has changed to include the files in /usr/src/*kernel_version* and /lib/modules/*kernel_version*. The calculated size for each kernel is now ~400 MB. The above code for rm-kernels and rm-kernels-server has been updated. However, the sample screens above do not reflect these changes yet.

The default is to estimate the size of files for linux-headers at 125 MB and linux-image at 220 MB because du can be painfully slow unless files are in cache. To get the real size using du pass any parameter to the script.

The total of all kernel sizes (excluding the current running version which cannot be removed) is now show in the title bar.

The dialog box used to display each Kernel's Last Access Date. This date can get mass overwritten for all kernels during backup or similar operations. The dialog box now shows the Modified Date instead.

4

I have a script for this that does not need very fancy string parsing.

Remove headers and images except the current one to release space

sudo apt-get autoremove --purge 'linux-headers-[0-9].*' linux-headers-$(uname -r)+ linux-headers-$(uname -r | cut -d- -f1,2)+ 'linux-image-[0-9].*' linux-image-$(uname -r)+
jarno
  • 6,175
3

An easy way to get rid of almost all obsolete packages, packages no longer in any package list, along with obsolete kernels is to do one of the following:

dpkg --purge $(aptitude search ?obsolete)

However, this will miss packages that are still recommended by other packages, and the -R/--without-recommends argument does not resolve this problem.

dselect after switching sort mode with 'o' will show all obsolete packages including the ones aptitude misses, but some people don't like using it.

Seth
  • 59,332
3

The accepted answer using sed to remove older kernels permanently has some flaws, if someone has not rebooted the computer after upgrading kernel the command will remove the newer kernel too.

Here is an alternate solution that will consider all situations to remove actual older kernels only:

#!/bin/bash
kernels=( $(grep -Po "^linux-image-[^-]+-[^-]+-generic\b" < <(dpkg --get-selections)) )
cur_rel=$(grep -Po ".*(?=-[a-z]*$)" < <(uname -r))

for kernel in "${kernels[@]}"; do
    ker_rel=$(grep -Po "[0-9].*(?=-[a-z]*)" <<< "$kernel")
    dpkg --compare-versions "$ker_rel" gt "$cur_rel" && echo "Please Restart your computer first" && break
    dpkg --compare-versions "$ker_rel" lt "$cur_rel" && sudo apt-get remove "$kernel"
done

If you have any version that is newer than the current one this will give you a warning to restart you computer first. Also note that the older kernels are preserved due to a good reason which is if you somehow mess up your current kernel making your system unstable then you should be able to boot into any older kernel.

heemayl
  • 93,925
2

Based on a previous answer by David Kemp, the following script will purge all headers and images except for the last 2 versions.

#!/bin/sh
# This script assumes that the installed linux-image and linux-headers packages
# share the same versions (i.e. if a linux-image version number is installed,
# the corresponding linux-headers package will also be installed, and vice
# versa.)

SECONDTOLASTVER=$(dpkg --list | grep linux-image | awk '{ print $2 }' | sort -r -n | sed '/^[^0-9]\+$/d' | sed 's/^.*-\([0-9\.]\+-[0-9]\+\).*/\1/' | uniq | sed -n 2p)

# get a list of package names matching the argument passed to the function, and
# return only those package names which should be removed
get_pkgs_to_remove_matching () {
    if [ -n "$SECONDTOLASTVER" ]; then
        echo $(dpkg --list | grep $1 | awk '{ print $2 }' | sort | sed -n '/'"$SECONDTOLASTVER"'/q;p')
    fi
}

echo $(get_pkgs_to_remove_matching linux-image) $(get_pkgs_to_remove_matching linux-headers) | xargs sudo apt-get purge

(use apt-get -y to remove without question)

rubo77
  • 34,024
  • 52
  • 172
  • 299
Mark
  • 1,479
2

ailurus has the feature of removing old kernels as well as unused configurations. I personally remove it manually from synaptic. You can install ailurus from getdeb as well as ppa

sagarchalise
  • 24,306
2

You can use ukuu - it's all GUI - to update and delete old Kernels. Works for me!

Just remember leave the last 2 installed and obviously the 'running' kernel.

You can also set ukuu to only show mainline releases, even RC kernels, hide point releases.

ukuu

You can find ukuu in Synaptic, or instructions are here:

OMG!Ubuntu ukuu install instructions

1

Install the synaptic package manager and go down to the filters tab (I think filters, if not try all 5) and select "local". This will show you orphaned packages on your system, such as the kernels. After you uninstall them, run update-grub. That command updates the list of boot options for grub.

If this fails, you can always try apt-get remove linux-image-version-generic.

Mateo
  • 8,152
1

To have a bit more control over which versions to keep, explicitly select the ones you want to remove. For instance if you want to remove kernel versions 3.2.0.[49-53], use a simple for loop:

for k in 49 51 52 53 ; do aptitude remove --purge linux-image-3.2.0-${k}-generic ; done

Adjust the list of kernel versions to fit.

1

Try this. Run it as root.

Save this script as, say ./keep-n-kernels.sh

Pass, as a command line argument, the number of most recent kernels you want to preserve.

    
#!/bin/bash

# pass n as a command line argument, and it will find all the installed
# kernels and keep only the n most recent ones => uninstall all older ones

# dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d'
# this command gives the list of all packages EXCEPT for the latest kernel.
# source : https://help.ubuntu.com/community/Lubuntu/Documentation/RemoveOldKernels

n=$1

# find the installed kernel versions :
# dpkg-query -W -f='${Version}\n' 'linux-image-*' | grep . | sort -n
# gives version numbers, one in each line
# dpkg-query -W -f='${Version}\n' 'linux-image-*' | grep . | sed 's/\...$//g' | grep -v '\...$'| sort -u
# gives only the ones that appear in linux-image

# suffix, e.g. -generic-pae
# the kind of kernel you boot into
suffix=$(uname -r | sed 's:^[0-9]\.[0-9]\.[0-9]\-[0-9]\{2\}::g')

command="apt-get purge "

for version in $(dpkg-query -W -f='${Version}\n' 'linux-image-*' | grep . | sed 's/\...$//g' | grep -v '\...$'| sort -u | head -n -${n})
do
    command=${command}"^linux-image-${version}${suffix} "
done

$command

Sample usage :

# ./keep-n-kernels.sh 4 # launch the apt-get command to remove all but the 4 most recent kernels

If you want [AND AT YOUR OWN RISK], you can add a -y (or a force flag) to the apt-get command and make it non-interactive.

Sankalp
  • 101
1

I'm using a KDE desktop, and the easiest option I found was using the kde-config-grub2 application as suggested here: https://www.kubuntuforums.net/showthread.php?58075-remove-old-linux-versions (which I already had installed for setting background image, default boot option, and the like). Next to the drop-down box where you can choose the default entry, there is a "Remove Old Entries" button. Clicking this button presents you with a list of all installed kernels and you can select which ones to remove. When you apply the changes it will use dpkg to actually remove them from the system as well as the GRUB menu.

Ben
  • 832
1

Just to chime in, you can also issue

apt-get remove linux-{image,headers}-x.y.z-{1,2,...,n}

as root, and the job will be done.

Severo Raz
  • 6,111
1

Next time, when removing old kernels open a Terminal and use this command:

sudo apt-get autoremove linux-headers-2.6.38-10-generic

You can use Synaptic to get the exact name of the kernel that you intend to delete. Just open Synaptic and search for "linux-headers" and then select which kernel entry you want to remove. The relevant entry will be tagged with "-generic" at the end.

To clear out any unused (left over) dependencies throughout the system use this command by itself:

sudo apt-get autoremove
Chad--24216
  • 1,231
1

here is a rough outline of what I did, careful as I am no expert in linux, be sure you know what you are doing and have backed up any files you are modifying.

gedit /boot/grub/grub.cfg

then find the entries you want to keep, we will highlight and copy them

cd /etc/grub.d
ls

you'll see a list of files like 10_linux and 30_os-prober

sudo chmod -x 10_linux

this will stop form auto adding all the linux entries into the grub boot menu.

gksudo gedit 40_custom

open the custom boot menu file, then go back to grub.cfg (which should still be open in gedit), and copy the entries you want to keep... such as

menuentry "My Default Karmic" {
  set root=(hd0,1)
  search --no-floppy --fs-uuid --set cb201140-52f8-4449-9a95-749b27b58ce8
  linux /boot/vmlinuz-2.6.31-11-generic root=UUID=cb201140-52f8-4449-9a95-749b27b58ce8 ro quiet splash
  initrd /boot/initrd.img-2.6.31-11-generic
}

paste them into 40_custom, and then save it.

sudo chmod 755 40_custom

makes it executable, then finally we update grub which will change the grub.cfg file:

sudo update-grub

Now, BEWARE, if you update your kernel or OS, your boot menu probably will not update... you'll have to do that manually. But doing this procedure will let you customize the boot menu a bit more, such as remove the kernel version and just put the ubuntu name... i.e. Ubuntu Lucid 10.04, etc...

Hope someone finds this helpful, as it took me a while to figure out... didn't see this solution anywhere...

Joe
  • 11
0

As @jarno and @earcam mentioned in their comments on the question, you first want to ensure all your linux kernel packages are marked as having been autoinstalled:

sudo apt-mark auto '^linux-.*-4\.12\.0-12(-generic)?$'

Then the normal apt-get autoremove command should work.

sudo apt-get autoremove --purge

Read the docs for more details.

hobs
  • 562
0

Read all the answer before executing the commands.

How I did it :

First get the kernal you are using uname -r

List the kernels, and note down the kernel that is just older (version 5.15.0-35 when you run the 5.15.0-36 - see the 35 and 36 at the end?) and another one: 5.14.0-46 (the latest of the 5.14 serie). That way, you want to keep two versions to revert to if needed).

(as a reminder : grep -v : if you want to find something which doesn’t match a given pattern, grep allows doing just that with -v flag)

 dpkg --list | egrep -i 'linux-image|linux-headers'

Remove the kernel unwanted. Note that the

 sudo apt-get --purge remove $(dpkg --list | egrep -i 'linux-image|linux-headers' | awk '/ii/{ print $2}' | grep -v `uname -r` | grep -v 5.15.0-35 | grep -v 5.14.0-46 )

the following might not be necessary, but it freed up space for me too - even if it should be the same than the set of command before

 dpkg -S "linux-image" 
 sudo apt-get --purge remove $(dpkg -S "linux-image" | cut -f'1' -d':' | grep -v 5.11 | grep -v 5.15.0-35 | grep -v 5.14.0-46 )

At the end, do :

 sudo apt autoremove
Cedric
  • 101
0

I'm using this remove_kernel script for removing old versions of kernel:

#!/usr/bin/env bash

if [ -z "${1}" ]; then echo 'Choose one of kernels as command line parameter:' ls -l /boot | grep initrd | grep '^-r' | awk -F'-' '{print$8"-"$9}' exit 1 else KRNL="${1}" fi

echo 'These files will be deleted:' echo '------' dpkg --list | egrep -i "$KRNL" | awk '{print$2}' echo '------' echo " Version to remove: ${KRNL}" echo " Current version: $(uname -r | awk -F'-' '{print$1"-"$2}')" echo '------' printf 'Do you want to proceed [enter y to continue]: ' read iamagree

if [ "${iamagree}x" == "yx" ]; then sudo apt-get -y --purge remove $(dpkg --list | egrep -i "$KRNL" | awk '{print$2}' | xargs) else echo "cancelled" fi

Usage:

$ remove_kernel
Choose one of kernels as command line parameter:
5.19.0-35
5.19.0-38
$ remove_kernel 5.19.0-35
These files will be deleted:
------
linux-headers-5.19.0-35-generic
linux-hwe-5.19-headers-5.19.0-35
linux-image-5.19.0-35-generic
linux-modules-5.19.0-35-generic
linux-modules-extra-5.19.0-35-generic
------
  Version to remove: 5.19.0-35
  Current version: 5.19.0-38
------
Do you want to proceed [enter y to continue]: y
...

Checked with Ubuntu 22.04.2 LTS

rzlvmp
  • 146
0

The following string of commands will purge any installed linux kernels except the currently running one (grep -v uname -r) and the lastest available kernel (dpkg -l | .... | tail -1):

dpkg -l | grep -E linux-image-.*-generic | cut -d ' ' -f3 | grep -v `dpkg -l | grep -E linux-image-.*-generic | cut -d ' ' -f3 | tail -1` | grep -v `uname -r` | xargs apt-get -y purge

I use this to keep desktop's boot volumes relatively clean, but in a server situation you'd probably want to expand the logic and write some additional scripting to maintain a list of the last X kernels the server has booted.

Something like a startup script that does:

uname -r >> /root/bootedkernels
cat /root/bootedkernels | sort -u | tail -3 > /root/bootedkernels # Keep the last 3 booted kernels

and then use:

dpkg -l | grep -E linux-image-.*-generic | cut -d ' ' -f3 | grep -vf /root/bootedkernels | grep -v `dpkg -l | grep -E linux-image-.*-generic | cut -d ' ' -f3 | tail -1` | grep -v `uname -r` | xargs apt-get -y purge
0

If you are using ansible to control the machines, this playbook might be helpful.

---
 - hosts: all
   become: yes
   tasks:
    - name: "Run apt-get autoremove"
      apt: 
        name: linux-image-generic
        autoremove: yes
        state: present 
        update_cache: yes
        cache_valid_time: 3600
flickerfly
  • 7,509
0

Rather use apt, the cli frontend for humans, than apt-get to purge old kernel images:

$ sudo apt autoremove --purge
...
The following packages will be REMOVED:
  linux-headers-4.4.0-51* linux-headers-4.4.0-51-generic* linux-image-4.4.0-51-generic* linux-image-extra-4.4.0-51-generic*
k0pernikus
  • 6,336
0

I use unattended-upgrades so I really liked Qwerty's answer that configures it to uninstall old kernel packages automatically.

However there was one issue: after a new kernel is installed, this process would uninstall the package for the currently running kernel!

That doesn't break the OS entirely. In fact I rarely noticed it had happened, until I needed to hot-load a module (e.g. by plugging in a USB device) and then the module could not be found.

As a solution to this, I created a script to generate a virtual package currently-installed-kernel which effectively pins the current kernel package until the next boot.

The script is called pin-current-kernel-with-package.sh. Here is the latest version (probably) and the first version (definitely).

Just save that script somewhere, and add it to your bootup process. For example, save it in /usr/local/bin/pin-current-kernel-with-package.sh and then edit /etc/rc.local and add the following line:

bash /usr/local/bin/pin-current-kernel-with-package.sh

Now your running kernel package will never be uninstalled!

joeytwiddle
  • 2,009
0

I give two safer variations of the methods in other answers: one using synaptic manager and the other using terminal.

With these variations you only remove packages that the system identifies as removable. There is no risk you remove linux kernels that are currently used—as it happened to me some weeks ago ;-(

Using Synaptic Manager

Instead of searching linux-image over all installed packages, select the autoremovable packages (See red ellipse in the picture below). Now from this list remove the linux-image you want.

enter image description here

Using terminal

Similarly, if you are removing kernels on terminal, show only the autoremovable kernels by running

 sudo apt-get -s autoremove | grep linux-image

And then remove whichever kernel linux-image-x.x.x-x-generic you want to remove from the given list by running

 sudo apt-get purge linux-image-x.x.x-x-generic 
0

To remove the kernels that were automatically installed via regular system updates, open terminal and run:

sudo apt autoremove --purge

To know your current kernel release run uname -r and also u can run uname --help to read more about all uname commands

You can also run df -h to show you file system drivers, similarly you can run df --help for more.

To enable automatic removing of old kernels: Edit the config file using gksu

gksudo gedit /etc/apt/apt.conf.d/50unattended-upgrades

When the file opens, uncomment the following line and change the value to true:

//Unattended-Upgrade::Remove-Unused-Dependencies "false";

If you need to install gksu, you can run sudo apt install gksu

To clear old packages we can use sudo apt-get clean

For More, visit http://ubuntuhandbook.org/index.php/2016/05/remove-old-kernels-ubuntu-16-04/

-2

Here's the script I use to keep 2 kernel packages installed (i.e. the current one and the previous one). It also removes the initrd-VERSION-generic.old-dkms files left around by DKMS, which can fill-up a small boot partition.

The script:

echo "**Removing -generic.old-dkms files from /boot**" && rm -f /boot/*-generic.old-dkms # if using DKMS it creates initrd-VERSION-generic.old-dkms in /boot and doesn't clean them up, meaning after a few kernel upgrades /boot can become full
OLDKERNEL=$(ls -tr /boot/vmlinuz-* | head -n -2 | cut -d- -f2- | awk '{print "linux-image-" $0}')
OLDHEADERS=$(ls -tr /boot/vmlinuz-* | head -n -2 | cut -d- -f2- | sed 's/-generic//g' | awk '{print "linux-headers-" $0}')
OLDHEADERS=${OLDHEADERS//-pae/} # remove -pae string as linux-header packages don't have it in their names
if [ -n "$OLDKERNEL" -o -n "$OLDHEADERS" ]; then
apt-get -q remove --purge $OLDKERNEL $OLDHEADERS
echo "**Finished removing old kernels**"
else
echo "**No old kernels found**"
fi
happyskeptic
  • 1,046