I needed to do this, as with over a year of using Linux on a Chromebook, /usr/lib/modules was taking over 10GiB of disk space (which is at a premium on this device) and I am only ever going to be running a single kernel instance anyway. The disk space necessary to run a single kernel was only ~500MiB, for contrast, ~5% of the disk space actually being used.
I came up with a solution myself, based on my distribution (KDE Neon, Ubuntu LTS-based), which consists of multiple Bash commands to do the following:
- Detect the currently running Linux version
- Detect
linux-modules-*
packages that belong to Linux versions not currently running
- Uninstall all
linux-modules-*
packages (and depending linux-image-*
packages) which are not running
Note that I've only tested this when already running the latest Linux kernel installed on my distribution. Your distribution might come with packages that depend on a meta-package, such as linux-generic-hwe-20.04
for Ubuntu 20.04 LTS-based distributions, and I haven't tested this script when running an older Linux after installing a newer one.
TL;DR:
sudo apt remove $(dpkg-query --show 'linux-modules-*' | cut -f1 | grep -v "$(uname -r)")
OK, But What Does That Mean?
The first command to run is uname -r
. This prints the release number of your currently-running Linux kernel, in a format such as this:
$ uname -r
5.15.0-48-generic
Find out more at man uname
.
The second command to look at is dpkg-query
, which lets us query installed packages in different ways. You can use --list
for a pretty, interactive display, but I chose to use --show
to make scripting simpler.
$ dpkg-query --show 'linux-modules-*'
linux-modules-5.11.0-44-generic 5.11.0-44.48~20.04.2
linux-modules-5.11.0-46-generic 5.11.0-46.51~20.04.1
# etc.
Find out more at man dpkg-query
.
A determined reader could probably figure out how to use the --showformat
option to skip this step, but I preferred a more familiar approach - the cut
command. The cut
command simply takes standard input, an optional delimiter provided with -d
(a tabulator character by default), and a field argument provided with -f
. In this case, it takes the output of our dpkg-query
command and returns the first column:
$ dpkg-query --show 'linux-modules-*' | cut -f1
linux-modules-5.11.0-44-generic
linux-modules-5.11.0-46-generic
# etc.
Find out more at man cut
.
We pipe the output of that into the grep
command, which lets us select lines that match a certain pattern - or, in this case, lines that don't match a certain pattern, using the -v|--invert-match
option. The "$(uname -r)"
returns a line like 5.15.0-48-generic
, which is then used as the pattern to grep
. This is how we select all linux-modules
packages which are not currently running.
$ dpkg-query --show 'linux-modules-*' | cut -f1 | grep -v "$(uname -r)"
# = dpkg-query --show 'linux-modules-*' | cut -f1 | grep -v "5.15.0-48-generic"
linux-modules-5.11.0-44-generic
linux-modules-5.11.0-46-generic
# etc.
Find out more at man grep
.
Finally, we just use this list as arguments to the sudo apt remove
command, which is probably so familiar to people at this point it doesn't need much explanation:
$ sudo apt remove $(dpkg-query --show 'linux-modules-*' | cut -f1 | grep -v "$(uname -r)")
# = sudo apt remove linux-modules-5.11.0-44-generic linux-modules-5.11.0-46-generic [...]
... but in case those are not familiar to you, man sudo
allows you to run a command as root, the system administrator (or theoretically another user), which is necessary to use man apt
, which manages packages and dependencies on Debian (hence Ubuntu)-based distributions.