How does 'apt' decide how many old kernels to keep?

I'm managing a couple of servers for core services (NTP, DNS, etc) and it just occured to me that one of the servers seems to keep the 3 latest kernels, instead of 2 on the others :

nul@quark:~$ sudo apt-get autoremove --purge
Reading package lists... Done
Building dependency tree       
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
nul@quark:~$ dpkg -l |grep linux-image
ii  linux-image-3.13.0-51-generic       3.13.0-51.84                     amd64        Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-3.13.0-52-generic       3.13.0-52.85                     amd64        Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-3.16.0-37-generic       3.16.0-37.49~14.04.1             amd64        Linux kernel image for version 3.16.0 on 64 bit x86 SMP
ii  linux-image-extra-3.13.0-51-generic 3.13.0-51.84                     amd64        Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-extra-3.13.0-52-generic 3.13.0-52.85                     amd64        Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-extra-3.16.0-37-generic 3.16.0-37.49~14.04.1             amd64        Linux kernel extra modules for version 3.16.0 on 64 bit x86 SMP
ii  linux-image-generic                 3.13.0.52.59                     amd64        Generic Linux kernel image
ii  linux-image-generic-lts-utopic      3.16.0.37.29                     amd64        Generic Linux kernel image

...

nul@dwarf:~$ sudo apt-get autoremove --purge
Reading package lists... Done
Building dependency tree       
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
nul@dwarf:~$ dpkg -l |grep linux-image
ii  linux-image-3.13.0-52-generic       3.13.0-52.85                     amd64        Linux kernel image for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-3.16.0-37-generic       3.16.0-37.49~14.04.1             amd64        Linux kernel image for version 3.16.0 on 64 bit x86 SMP
ii  linux-image-extra-3.13.0-52-generic 3.13.0-52.85                     amd64        Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
ii  linux-image-extra-3.16.0-37-generic 3.16.0-37.49~14.04.1             amd64        Linux kernel extra modules for version 3.16.0 on 64 bit x86 SMP
ii  linux-image-generic                 3.13.0.52.59                     amd64        Generic Linux kernel image
ii  linux-image-generic-lts-utopic      3.16.0.37.29                     amd64        Generic Linux kernel image

All servers are maintained identically, don't know what I could have done, must be a parameter somewhere but can't find it!

Please feed my curiosity! Thank you


There is a file that is auto-generated that tells apt-get what kernels to autoremove and which ones to keep.
The file that tells apt-get which kernels they are is /etc/apt/apt.conf.d/01autoremove-kernels which is generated from /etc/kernel/postinst.d/apt-auto-removal.

Usually what happens is that when you are receiving kernel updates, when the kernel version changes, say from 3.13 to 3.16, /etc/apt/apt.conf.d/01autoremove-kernels is then updated to keep the 3.16* kernels and is then set to remove all of the 3.13 kernels unless specified by the generating script to not be removed.

From the apt-auto-removal script:

# Author: Steve Langasek 
#
# Mark as not-for-autoremoval those kernel packages that are:
#  - the currently booted version
#  - the kernel version we've been called for
#  - the latest kernel version (determined using rules copied from the grub
#    package for deciding which kernel to boot)
#  - the second-latest kernel version, if the booted kernel version is
#    already the latest and this script is called for that same version,
#    to ensure a fallback remains available in the event the newly-installed
#    kernel at this ABI fails to boot
# In the common case, this results in exactly two kernels saved, but it can
# result in three kernels being saved.  It's better to err on the side of
# saving too many kernels than saving too few.
#
# We generate this list and save it to /etc/apt/apt.conf.d instead of marking
# packages in the database because this runs from a postinst script, and apt
# will overwrite the db when it exits.

However, this sometimes will not mark them for auto-removal since some of the coding has changed over versions to prevent this from happening.

If you want mark the previous kernels for autoremove except for the required kernels based on the scripts, run the following command from a terminal window:

sudo apt-mark auto ^linux-image-

Then, when you run the apt-get autoremove command only the ones that are old and no longer needed can be removed. I have put examples below:

This first one shows all kernels on the system minus the current running kernel.

root@terrance-Linux:/home/share# dpkg -l 'linux-*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\(.*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d'
linux-headers-3.16.0-34
linux-headers-3.16.0-34-generic
linux-headers-3.16.0-36
linux-headers-3.16.0-36-generic
linux-headers-3.16.0-37
linux-headers-3.16.0-37-generic
linux-headers-4.0.0-040000
linux-headers-4.0.0-040000-generic
linux-image-3.16.0-34-generic
linux-image-3.16.0-36-generic
linux-image-3.16.0-37-generic
linux-image-4.0.0-040000-generic
linux-image-extra-3.16.0-34-generic
linux-image-extra-3.16.0-36-generic
linux-image-extra-3.16.0-37-generic

This one shows the current running kernel.

terrance@terrance-Linux:~$ uname -r
4.0.1-040001-generic
terrance@terrance-Linux:~$ sudo apt-get autoremove
[sudo] password for terrance: 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
terrance@terrance-Linux:~$ sudo apt-mark auto ^linux-image-
linux-image-extra-3.16.0-33-generic can not be marked as it is not installed.
linux-image-extra-3.13.0-27-generic can not be marked as it is not installed.
linux-image-3.13.0-44-lowlatency can not be marked as it is not installed.
linux-image-3.13.0-27-generic can not be marked as it is not installed.
linux-image-3.16.0-31-lowlatency can not be marked as it is not installed.
linux-image-3.16.0-36-generic set to automatically installed.
linux-image-lowlatency-lts-utopic can not be marked as it is not installed.
linux-image-extra-3.13.0-36-generic can not be marked as it is not installed.
linux-image-3.13.0-36-generic can not be marked as it is not installed.
linux-image-4.0.0-040000-generic set to automatically installed.
linux-image-extra-3.13.0-45-generic can not be marked as it is not installed.
linux-image-3.16.0-25-generic can not be marked as it is not installed.

NOTE: The above one was too long to list, so I truncated a bit.

terrance@terrance-Linux:~$ sudo apt-get autoremove
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be REMOVED:
  linux-image-3.16.0-34-generic linux-image-3.16.0-36-generic
  linux-image-4.0.0-040000-generic linux-image-extra-3.16.0-34-generic
  linux-image-extra-3.16.0-36-generic
0 upgraded, 0 newly installed, 5 to remove and 0 not upgraded.
After this operation, 613 MB disk space will be freed.
Do you want to continue? [Y/n] 

So, after running those commands, you can see that I can now auto-remove all the old but the current kernel (4.0.1-040001-generic) and the next newest one (3.16.0-37-generic).

Hopefully this helps.