How To

Install or Update the Latest Linux Kernel on Ubuntu 26.04 / 24.04

Linux 7.3 landed on kernel.org on October 18, 2026, and the usual Ubuntu shortcut to it is broken. Canonical’s mainline build service at kernel.ubuntu.com still publishes the long-term branches, but it has not finished a 7.x build since September 14: the newest 7.x folder is v7.2.6, there is no v7.3 at all, and 7.3-rc3 was the last release candidate it built. To install or upgrade to the latest Linux kernel on Ubuntu this week, you need to know which of three routes still delivers it, and which one breaks on 24.04 before it finishes installing.

Original content from computingforgeeks.com - post 16087

This guide covers all three: the prebuilt mainline packages, the Zabbly repository, and a kernel.org source build packaged as .deb files, the only way onto 7.3 today. It also covers the Secure Boot failure, two 24.04 packaging bugs and a clean rollback. Every command was run on Ubuntu 26.04.1 LTS and 24.04.5 LTS in October 2026, ending with both booted into Linux 7.3.

Check your kernel, Secure Boot and /boot first

Three things decide whether a new kernel boots or leaves you at a GRUB prompt, so check them before downloading anything. Start with the kernel you are running now, because that is the one you fall back to and the one whose config the source build copies:

uname -r

A fully updated 26.04 server prints the 7.0 series that Ubuntu ships, and 24.04 prints the 6.8 GA kernel unless you moved to the HWE stack:

7.0.0-38-generic

Next, Secure Boot. None of the kernels in this guide is signed with a key your firmware trusts, so on a machine with Secure Boot on they will not boot, and the failure is not graceful. Check the state:

mokutil --sb-state

The 24.04 VM we used for the mainline and Zabbly tests boots UEFI with Microsoft keys enrolled, which is what most laptops ship with:

SecureBoot enabled

If you see SecureBoot enabled, either turn Secure Boot off before the reboot step, in the firmware setup on hardware or in the instance settings on a cloud VM, or stay on Ubuntu’s signed kernel. EFI variables are not supported on this system means a legacy BIOS boot, where Secure Boot does not apply. Last, check free space on /boot. Ubuntu cloud images put it on a separate partition of about 900 MB to 1 GB, and every kernel adds 70 to 80 MB of image and initramfs to it:

df -h /boot

Keep at least 200 MB free. Then make sure you can reach the GRUB menu if the new kernel fails. Ubuntu hides it by default with a zero timeout, and cloud images set GRUB_TIMEOUT=0 again in /etc/default/grub.d/50-cloudimg-settings.cfg, so picking the old kernel means pressing Esc as a UEFI machine starts, or holding Shift on a BIOS one, at a local console. Over a serial console only Esc works, because Shift cannot be sent. With a zero timeout the window is short, so arrange that access now, not after the reboot.

Pick a method

The three routes differ in how new a kernel they give you, how it gets updated, and whether DKMS modules such as the NVIDIA driver or VirtualBox still build. This is what each one delivered on our two machines:

MethodNewest kernel on Oct 21, 2026UpdatesUbuntu 26.04Ubuntu 24.04
kernel.ubuntu.com mainline7.2.6 (no 7.x builds since Sep 14)Manual downloadInstalls cleanNeeds two fixes: run-parts and gcc-15
Zabbly repository7.2.11, moves to 7.3 at 7.3.1apt, weeklyInstalls cleanInstalls clean, DKMS builds with stock gcc
Build from kernel.org7.3 (any release, same day)Rebuild per releaseBuilds and bootsBuilds and boots

For most people we’d pick Zabbly: it tracks the newest stable kernel, updates through apt like any other package, and its 24.04 builds use 24.04’s own compiler, so DKMS keeps working. It deliberately waits for the first bugfix release of a new series, which is why it still carries 7.2.11 days after 7.3 shipped. Build from source when you need 7.3 now, or a config change. Use the mainline packages on 26.04 if you only want to test a specific release, and avoid them on 24.04 unless you are happy patching package scripts.

Install a mainline build from kernel.ubuntu.com

Canonical builds upstream releases as plain .deb packages with no Ubuntu patches. Before downloading, compare the newest stable release on kernel.org with the newest folder the builder published, because right now they disagree:

curl -s https://www.kernel.org/finger_banner | awk '/latest stable version/{print $NF}'
KVER=$(curl -s https://kernel.ubuntu.com/mainline/ | grep -oE 'href="v[0-9]+\.[0-9]+(\.[0-9]+)?/"' | grep -oE 'v[0-9.]+[0-9]' | sort -V | tail -1)
echo "${KVER}"

kernel.org reports 7.3 while the newest mainline build is 7.2.6, which is the 7.x stall described above. The LTS folders the builder still updates, such as v6.18.58, sort below it, and the grep skips -rc folders, so KVER always holds the newest released build:

7.3
v7.2.6

Each release folder has an amd64 directory with build timestamps in the file names, so pull the list from the directory page instead of typing names. Before you fetch a different version, clear out the old one with rm -f ~/mainline-kernel/*.deb, because the commands below expect exactly one image package in that directory. You need four generic packages: two header packages, the image and the modules. The lowlatency flavour is skipped on purpose:

mkdir -p ~/mainline-kernel && cd ~/mainline-kernel
BASE="https://kernel.ubuntu.com/mainline/${KVER}/amd64"
curl -s "${BASE}/" | grep -oE 'linux-(headers|image-unsigned|modules)-[^"]*(generic_[^"]*_amd64|_all)\.deb' | sort -u > debs.txt
sed "s|^|${BASE}/|" debs.txt | wget -q -i -
ls -1 *.deb

The four files that land are about 208 MB together, most of it the modules package:

linux-headers-7.2.6-070206-generic_7.2.6-070206.202609141300_amd64.deb
linux-headers-7.2.6-070206_7.2.6-070206.202609141300_all.deb
linux-image-unsigned-7.2.6-070206-generic_7.2.6-070206.202609141300_amd64.deb
linux-modules-7.2.6-070206-generic_7.2.6-070206.202609141300_amd64.deb

Install them with apt rather than dpkg -i, because apt pulls in wireless-regdb, which the 24.04 install needed and dpkg would have left unconfigured:

sudo apt install -y ./*.deb

On 26.04 that is the whole job. The image package runs the kernel hooks, dracut builds the initramfs and GRUB picks up the new entry, which is the line to look for at the end:

dracut: Generating /boot/initrd.img-7.2.6-070206-generic
/etc/kernel/postinst.d/zz-update-grub:
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-7.2.6-070206-generic
Found initrd image: /boot/initrd.img-7.2.6-070206-generic
Found linux image: /boot/vmlinuz-7.0.0-38-generic
Found initrd image: /boot/initrd.img-7.0.0-38-generic

On 24.04 the same command ends in errors. Two separate things go wrong, and both have to be fixed before the kernel is usable.

Ubuntu 24.04: “run-parts: missing operand”

The install stops with both kernel packages half configured:

Setting up linux-headers-7.2.6-070206-generic (7.2.6-070206.202609141300) ...
run-parts: missing operand
Try `run-parts --help' for more information.
dpkg: error processing package linux-headers-7.2.6-070206-generic (--configure):
 installed linux-headers-7.2.6-070206-generic package post-installation script subprocess returned error exit status 1

The packages are built on a newer Ubuntu, and their maintainer scripts call run-parts with two directories, /etc/kernel/postinst.d and /usr/share/kernel/postinst.d. The run-parts in 24.04 (debianutils 5.17) accepts exactly one directory, while 26.04 ships 5.23.2, which takes both. The second directory does not exist on 24.04, so dropping it from every run-parts call of that kernel, including the pending trigger, loses nothing. Change only those calls. The scripts also test each directory with [ -d ... ], and a sed that strips the path from those tests turns them into an always-true [ -d ], which makes apt purge fail on any 24.04 box without DKMS. Set the kernel release string from the image package and patch the calls:

KREL=$(dpkg-deb -f ~/mainline-kernel/linux-image-unsigned-*.deb Package | sed 's/^linux-image-unsigned-//')
echo "${KREL}"
sudo sed -i -E 's#(/etc/kernel/[a-z_]+\.d) /usr/share/kernel/[a-z_]+\.d#\1#' /var/lib/dpkg/info/linux-*-"${KREL}".p* /usr/lib/linux/triggers/"${KREL}"

That fixes install and removal in one go, because the same two-directory call sits in prerm and postrm. Without the patch, apt purge fails later with the identical error. On a 24.04 machine with no DKMS modules, the install stops at the image package instead of the headers, and after the patch one command finishes it:

sudo dpkg --configure -a

We ran exactly that on a 24.04 VM without DKMS: all four packages reached ii, the trigger built the initramfs and updated GRUB, and a later apt purge removed the kernel cleanly. If you do have DKMS modules, hold off on dpkg --configure -a, because the second problem comes next.

Ubuntu 24.04: “gcc-15: not found” while DKMS builds

Mainline headers record the exact compiler the kernel was built with, and for the 7.x series that is gcc-15. Our 24.04 machine had v4l2loopback-dkms installed as a stand-in for NVIDIA or VirtualBox, and its build failed like this in /var/lib/dkms/v4l2loopback/0.12.7/build/make.log:

warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-15 (Ubuntu 15.2.0-12ubuntu1) 15.2.0
  You are using:
  CC [M]  v4l2loopback.o
/bin/sh: 1: gcc-15: not found
make[4]: *** [/usr/src/linux-headers-7.2.6-070206-generic/scripts/Makefile.build:289: v4l2loopback.o] Error 127

The 24.04 archive has no gcc-15, so it has to come from the Ubuntu Toolchain test PPA. Read apt’s upgrade list before you confirm the install: on 24.04 it also replaces the GCC runtime libraries, libstdc++6 included, with a GCC 16 snapshot, which the paragraph after the DKMS output explains. Add the PPA and install the compiler:

sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt install gcc-15

Expect that install to end in a dpkg error, because apt retries the pending kernel configuration before gcc-15 is in place. Finishing the configuration now clears it:

sudo dpkg --configure -a

With both fixes in place, every package shows ii and DKMS reports the module built for the new kernel:

v4l2loopback/0.12.7, 6.8.0-147-generic, x86_64: installed
v4l2loopback/0.12.7, 7.2.6-070206-generic, x86_64: installed

Be careful with that PPA. It is the Ubuntu Toolchain team’s unsupported test archive, not part of Ubuntu, and installing gcc-15 from it is not a compiler-only change. On our 24.04 VM, apt install gcc-15 also upgraded twelve GCC runtime libraries, libstdc++6, libgcc-s1 and libgomp1 among them, from 24.04’s 14.2 builds to a GCC 16 snapshot. The PPA’s gcc-15 needs newer runtime libraries than 24.04 ships, and the only newer ones it has come from GCC 16. Every C++ program on the machine then runs against a pre-release libstdc++. Removing the PPA later does not put the old versions back. Remove it once gcc-15 is in anyway, so nothing else from it arrives with the next apt upgrade:

sudo add-apt-repository -y --remove ppa:ubuntu-toolchain-r/test

gcc-15 stays installed, and DKMS keeps using it for later mainline builds. That runtime swap is the main reason we’d send 24.04 users to Zabbly instead.

Add the Zabbly kernel repository

Zabbly publishes the newest stable mainline kernel for Ubuntu 22.04, 24.04 and 26.04 and for Debian 12 and 13, built from a config derived from Ubuntu’s, and updates it about weekly. Because it is an apt repository, new point releases arrive with apt upgrade. Check the signing key’s fingerprint before trusting it:

curl -fsSL https://pkgs.zabbly.com/key.asc | gpg --show-keys --fingerprint

The fingerprint must match the one in the Zabbly kernel README, 4EFC 5906 96CB 15B8 7C73 A3AD 82CC 8797 C838 DCFD:

pub   rsa3072 2023-08-23 [SC] [expires: 2030-08-17]
      4EFC 5906 96CB 15B8 7C73  A3AD 82CC 8797 C838 DCFD
uid                      Zabbly Kernel Builds <[email protected]>
sub   rsa3072 2023-08-23 [E] [expires: 2030-08-17]

Save the key and add the repository. The release codename and architecture come from the running system, so the same line works on both LTS releases:

sudo mkdir -p /etc/apt/keyrings
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
printf 'Enabled: yes\nTypes: deb\nURIs: https://pkgs.zabbly.com/kernel/stable\nSuites: %s\nComponents: main\nArchitectures: %s\nSigned-By: /etc/apt/keyrings/zabbly.asc\n' "$(. /etc/os-release && echo "$VERSION_CODENAME")" "$(dpkg --print-architecture)" | sudo tee /etc/apt/sources.list.d/zabbly-kernel-stable.sources

Refresh the package lists and install the linux-zabbly metapackage, which always points at the current build:

sudo apt update
sudo apt install linux-zabbly

Read the transaction summary before you confirm. Besides the image and headers, Zabbly replaces Ubuntu’s linux-libc-dev with its own, newer copy. This is apt’s summary on 26.04; 24.04’s older apt lists the same packages under “The following NEW packages will be installed” and “The following packages will be upgraded”:

Upgrading:
  linux-libc-dev
Installing:
  linux-zabbly
Installing dependencies:
  linux-headers-7.2.11-zabbly+  linux-image-7.2.11-zabbly+

linux-libc-dev holds the kernel’s userspace API headers, which programs you compile on that machine include. Newer headers are normally harmless, but on a build server that produces binaries for stock Ubuntu you want Ubuntu’s copy, and removing Zabbly later does not put it back by itself (the removal section covers that). On 24.04 the DKMS build for v4l2loopback succeeded straight away, because Zabbly compiles its noble packages with noble’s gcc 13.3 rather than gcc-15.

Build the newest kernel from kernel.org source

Building from source is the only way to run 7.3 on Ubuntu before the packaged routes catch up, and the kernel’s own bindeb-pkg target turns the build into normal .deb packages, so apt can install and remove them. Starting from the running kernel’s config keeps Ubuntu’s driver selection, which is why the result behaves like a newer Ubuntu kernel rather than a minimal one. If you want to trim the config or understand each build stage in depth, our guide to compiling the Linux kernel from source goes through it; this section stays on the Ubuntu route. Check the disk before anything else. With Ubuntu’s debug info left on, the 7.3 tree on our 26.04 machine grew to 42 GB, and the finished packages added almost 2 GB next to it, so you need about 45 GB free under your home directory. Then install the build dependencies. libdw-dev is on the list because 7.3’s packaging refuses to start without it:

sudo apt install -y build-essential bc bison flex libssl-dev libelf-dev libdw-dev libncurses-dev dwarves debhelper-compat rsync cpio zstd python3 wget xz-utils gnupg

Read the current stable version from kernel.org, then download the tarball and its signature. The v7.x directory comes from the version’s major number, so the commands keep working for later releases:

KVER=$(curl -s https://www.kernel.org/finger_banner | awk '/latest stable version/{print $NF}')
echo "${KVER}"
mkdir -p ~/kernel-build && cd ~/kernel-build
wget "https://cdn.kernel.org/pub/linux/kernel/v${KVER%%.*}.x/linux-${KVER}.tar.xz"
wget "https://cdn.kernel.org/pub/linux/kernel/v${KVER%%.*}.x/linux-${KVER}.tar.sign"

The signature covers the uncompressed tarball, so decompress it into gpg rather than verifying the .xz file. Import both Linus Torvalds’ and Greg Kroah-Hartman’s keys, because either one may sign a release. The 7.3 tarball is signed by Greg’s key:

gpg --locate-keys [email protected] [email protected]
xz -cd "linux-${KVER}.tar.xz" | gpg --verify "linux-${KVER}.tar.sign" -

Look for Good signature and a primary key fingerprint that matches the key kernel.org lists. The “not certified with a trusted signature” warning only means you have not personally signed that key:

gpg: Signature made Mon Oct 19 05:45:26 2026 UTC
gpg:                using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Good signature from "Greg Kroah-Hartman <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 647F 2865 4894 E3BD 4571  99BE 38DB BDC8 6092 693E

Unpack the source, copy in the config of the kernel you are running, and clear the two Canonical certificate paths. Ubuntu’s config points CONFIG_SYSTEM_TRUSTED_KEYS at debian/canonical-certs.pem, a file that ships with Ubuntu’s kernel source but not in the kernel.org tarball, and the build stops three minutes in without it:

tar xf "linux-${KVER}.tar.xz"
cd "linux-${KVER}"
cp "/boot/config-$(uname -r)" .config
scripts/config --set-str SYSTEM_TRUSTED_KEYS "" --set-str SYSTEM_REVOCATION_KEYS "" --enable SCHED_CLASS_EXT
make olddefconfig

make olddefconfig answers every option that is new since your running kernel with its default, and drops options that no longer exist. The defaults are where the two releases part ways. On 24.04 the copied config comes from 6.8, which predates sched_ext, so without the --enable line olddefconfig left CONFIG_SCHED_CLASS_EXT unset and the kernel would have shipped without the BPF scheduler class that 26.04’s own kernel has. On 26.04 the option is already on, and the extra flag changes nothing. Ubuntu’s 26.04 config also sets CONFIG_RUST=y, which silently turns off without a Rust toolchain, so Rust-only drivers are not built. Keep debug info on even though it is what makes the tree so large: sched_ext depends on CONFIG_DEBUG_INFO_BTF, and stripping debug info removes it again. Confirm the settings that matter:

grep -E '^CONFIG_(SYSTEM_TRUSTED_KEYS|SYSTEM_REVOCATION_KEYS|DEBUG_INFO_BTF|SCHED_CLASS_EXT)=' .config

Both key paths should be empty, with BTF and sched_ext still on:

CONFIG_SCHED_CLASS_EXT=y
CONFIG_SYSTEM_TRUSTED_KEYS=""
CONFIG_SYSTEM_REVOCATION_KEYS=""
CONFIG_DEBUG_INFO_BTF=y

Build the packages. LOCALVERSION adds a suffix to the release string so the new kernel can never collide with an Ubuntu package of the same version:

make -j"$(nproc)" bindeb-pkg LOCALVERSION=-custom

This is the long step. On a 6 vCPU VM with 8 GB of RAM, the 24.04 build took 96 minutes from a clean tree, nearly all of it compiling the thousands of modules Ubuntu’s config enables, and the final 12 minutes went on packing the debug image. Start it inside tmux or screen on a remote machine, because a dropped SSH session takes the build down with it. The packages land one directory up, next to the tarball:

ls -lh ~/kernel-build/*.deb

Four packages come out of it, and you install only two of them. The -dbg image is 1.5 GB of debug symbols that only matter for crash-dump analysis, and linux-libc-dev would replace Ubuntu’s own userspace kernel headers, the same side effect the Zabbly section undoes:

-rw-r--r-- 1 josphat josphat  11M Oct 21 18:30 /home/josphat/kernel-build/linux-headers-7.3.0-custom_7.3.0-2_amd64.deb
-rw-r--r-- 1 josphat josphat 1.5G Oct 21 18:49 /home/josphat/kernel-build/linux-image-7.3.0-custom-dbg_7.3.0-2_amd64.deb
-rw-r--r-- 1 josphat josphat 117M Oct 21 18:37 /home/josphat/kernel-build/linux-image-7.3.0-custom_7.3.0-2_amd64.deb
-rw-r--r-- 1 josphat josphat 1.5M Oct 21 18:30 /home/josphat/kernel-build/linux-libc-dev_7.3.0-2_amd64.deb

Install the image and headers through apt, so they are tracked like any other package. The globs match the version in KVER and skip the -dbg file. If you opened a new shell since the download step, set KVER again first. The number after the version in each file name is a build counter that goes up every time you rebuild in the same tree, so delete older .deb files before installing a rebuild, or apt gets two versions of the same package:

cd ~/kernel-build
sudo apt install ./linux-image-"${KVER}"*-custom_*.deb ./linux-headers-"${KVER}"*-custom_*.deb

apt reports about 712 MB of new disk use, almost all of it modules under /usr/lib/modules. The image package’s hooks build the initramfs, with update-initramfs on 24.04 and dracut on 26.04, and then run update-grub, so the new kernel is in the boot menu as soon as apt returns. On 24.04, /boot grew by 79 MB, because the initramfs for a kernel with Ubuntu’s full module set came out at 53 MB, against 40 MB on 26.04.

“No rule to make target ‘debian/canonical-certs.pem'”

If you skip the scripts/config line, this is where the build dies, after about three minutes of compiling:

make[6]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'.  Stop.

Run the scripts/config --set-str command from above, then make olddefconfig, and start the build again. Nothing before that point needs cleaning. The other early failure, dpkg-checkbuilddeps: error: unmet build dependencies: libdw-dev:native, appears within a second of starting and means libdw-dev was missing from the dependency install.

Boot the new kernel and verify it

GRUB boots the highest version by default, so a plain reboot is enough on a machine where you have console access:

sudo reboot

When the machine is back, confirm which kernel it is running. uname -r prints the release with your LOCALVERSION suffix, and uname -v carries the build number and build time, which is how you tell a rebuild from the previous one:

uname -r
uname -v

Both build VMs came up on the new kernel. They boot with legacy BIOS, so Secure Boot never came into it there. This is the 24.04 one:

7.3.0-custom
#2 SMP PREEMPT_DYNAMIC Wed Oct 21 18:13:07 UTC 2026

We also checked, on the 26.04 machine, that the scheduler pieces from the config made it into the running kernel. sched_ext is present, and disabled only means no BPF scheduler is loaded yet. The cgroup_mode switch covered in the next section is there too:

uname output showing Linux 7.3 custom kernel running on Ubuntu 26.04 with sched_ext and cgroup_mode

The stock kernel stays installed and stays in the GRUB menu under “Advanced options for Ubuntu”, so a kernel that panics or loses a driver costs you one reboot, not the machine. Keep it until the new one has run your real workload for a few days. If the machine does not come back at all and Secure Boot is on, go straight to the last section.

What Linux 7.3 changes on an Ubuntu box

Most of 7.3 is driver work and internal rework that a server never shows you. For an earlier cycle in more depth, see what changed in Linux kernel 7.1. Three changes are visible from userspace on Ubuntu, and we checked each one on the running 7.3 kernel, with the stock 24.04 kernel as the control:

  • a new fchroot system call and the failfs sandbox root
  • a new default for how a cgroup’s CPU weight is spread across CPUs
  • the freevxfs and efs filesystem drivers, removed

The first is new API for sandboxes, the second changes how busy containers and services share CPU on an unchanged system, and the third breaks one specific job, mounting old Veritas or SGI media.

fchroot and failfs

7.3 adds fchroot, system call 472 on x86_64, which sets the root directory from a file descriptor instead of a path. Its interesting use is a special descriptor, FD_FAILFS_ROOT, that moves the process root into failfs, a kernel-internal filesystem where every lookup fails. After that, an absolute path such as /etc/os-release resolves to nothing, while directories the process opened beforehand keep working. A sandboxed parser or worker can be locked to the descriptors it was handed, and an unprivileged process may do it as long as it has set no_new_privs first, is not already in a chroot, and does not share its filesystem context with another thread, which rules out multithreaded workers. To see it, save this script as ~/failfs-test.py:

vim ~/failfs-test.py

It tries the call without no_new_privs, then with it, and then reads the filesystem both ways:

import ctypes, os, errno
libc = ctypes.CDLL(None, use_errno=True)
SYS_fchroot, FD_FAILFS_ROOT, PR_SET_NO_NEW_PRIVS = 472, -10004, 38
def fchroot():
    r = libc.syscall(SYS_fchroot, ctypes.c_int(FD_FAILFS_ROOT), ctypes.c_uint(0))
    e = ctypes.get_errno()
    return "0" if r == 0 else f"-1 {errno.errorcode.get(e, e)}"
print("fchroot without no_new_privs:", fchroot())
libc.prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
home = os.open(os.path.expanduser("~"), os.O_RDONLY | os.O_DIRECTORY)
print("fchroot with no_new_privs:   ", fchroot())
for label, fn in [("listdir('/etc')", lambda: os.listdir("/etc")),
                  ("open('/etc/os-release')", lambda: open("/etc/os-release")),
                  ("os.listdir(held home fd)", lambda: sorted(os.listdir(home))[:3])]:
    try:
        print(f"{label}: {fn()}")
    except OSError as ex:
        print(f"{label}: {errno.errorcode[ex.errno]}")

Run it as your normal user, not with sudo, because the unprivileged rules are the point:

python3 ~/failfs-test.py

On 7.3 the first call is refused with EPERM, the second succeeds, and from then on only the descriptor opened before the switch can read anything:

fchroot without no_new_privs: -1 EPERM
fchroot with no_new_privs:    0
listdir('/etc'): ENOTSUP
open('/etc/os-release'): ENOTSUP
os.listdir(held home fd): ['.bash_logout', '.bashrc', '.cache']

The same script on 24.04’s 6.8 kernel gets ENOSYS for both calls and goes on to list /etc normally, so software that wants failfs has to check for that error and fall back. Python prints ENOTSUP, which on Linux is the same errno as the EOPNOTSUPP the kernel documentation names. That documentation also warns that getting back out of failfs is hard for now, so treat the switch as one way. This is an interface for sandbox and container runtimes; you’d only call it directly when writing one.

cgroup CPU weight gets a new default

This one changes behaviour without anyone touching a setting. Every systemd slice and container cgroup has a CPU weight, 100 by default, and the scheduler has to decide how that weight is shared when the group’s tasks run on several CPUs. Up to 7.2 the weight was split across those CPUs, so a group running six busy tasks carried only a share of its weight on each one, and a single-task group with the same weight beat it wherever the two met. 7.3 adds a debugfs switch with five modes and makes concur the default, which scales the group’s weight by the number of CPUs it is actually busy on:

sudo cat /sys/kernel/debug/sched/cgroup_mode

The mode in brackets is the active one, and smp matches the 7.2 calculation:

up smp (concur) max tasks

To see what that does, we ran two slices with the same weight of 100 on the 6 vCPU 26.04 machine. One slice ran six busy loops and the other ran one, and we read each slice’s cpu.stat over 20 seconds in both modes:

cgroup_modeSlice with 6 busy tasksSlice with 1 busy task
smp (7.2 behaviour)500% of one CPU100% of one CPU
concur (7.3 default), run 1515% of one CPU86% of one CPU
concur (7.3 default), run 2519% of one CPU83% of one CPU

Under the old calculation the single-task slice kept a whole CPU to itself. Under concur the group with more runnable work took about 15% of a CPU away from it, with both weights unchanged. On a host that runs one heavily threaded service next to small, latency-sensitive ones, that is a real shift. If a small service gets slower after the upgrade, check this before blaming anything else, and give it a higher CPUWeight= rather than relying on the old split. To compare with the old behaviour, switch the mode at runtime. The setting lives in debugfs and returns to concur on the next boot:

echo smp | sudo tee /sys/kernel/debug/sched/cgroup_mode

Write concur to the same file when you have finished comparing, so the rest of the uptime runs on the 7.3 default you will get after every reboot anyway.

freevxfs and efs are gone

Ubuntu’s 7.0 kernel builds the read-only Veritas VxFS driver and the SGI EFS driver as modules. 7.3 removes both from the source tree, so a config copied from Ubuntu loses them and nothing warns you:

sudo modprobe freevxfs

modprobe can only report the module as missing:

modprobe: FATAL: Module freevxfs not found in directory /lib/modules/7.3.0-custom

efs fails the same way. Almost nobody mounts these, but if you still read VxFS disks from SCO UnixWare or HP-UX, or CDs from SGI IRIX, keep a 7.2 or older kernel around for that job.

Remove a kernel you installed

Boot into the kernel you are keeping before you remove anything, so you never purge the files of the kernel that is running. On a headless server you don’t need the GRUB menu for that: grub-reboot picks the entry for the next boot only, and it works with Ubuntu’s default GRUB_DEFAULT=0. List the entry names:

sudo grep -oE "(menuentry|submenu) '[^']+'" /boot/grub/grub.cfg | cut -d"'" -f2

Stock kernels sit under the “Advanced options for Ubuntu” submenu:

Ubuntu
Advanced options for Ubuntu
Ubuntu, with Linux 7.2.11-zabbly+
Ubuntu, with Linux 7.2.11-zabbly+ (recovery mode)
Ubuntu, with Linux 7.2.6-070206-generic
Ubuntu, with Linux 7.2.6-070206-generic (recovery mode)
Ubuntu, with Linux 6.8.0-147-generic
Ubuntu, with Linux 6.8.0-147-generic (recovery mode)
UEFI Firmware Settings

Join the submenu and entry with >, set it for the next boot, and reboot. Put your own stock version in place of 6.8.0-147:

sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 6.8.0-147-generic"
sudo reboot

After that one boot, next_entry in grub-editenv list is empty again, so a later reboot returns to the default. Now purge by exact package name, and let dpkg-query produce the list, so the image, headers and modules packages all go together. For a mainline build, match its upload number (the 070206 part):

sudo apt purge $(dpkg-query -W -f='${Package}\n' 'linux-*070206*')

On 24.04, that only works if you ran the run-parts patch from the install section, because the removal scripts carry the same two-directory call. dpkg prints directory '/usr/lib/modules/7.2.6-070206-generic' not empty so not removed while it works through the packages, with or without DKMS. On both of our 24.04 VMs the directory was gone once the purge finished. Check afterwards that only kernels you still have installed are listed:

ls -d /usr/lib/modules/*

A directory for a kernel you removed is safe to delete with sudo rm -rf. For Zabbly, purge its packages, remove the repository, and put Ubuntu’s linux-libc-dev back. apt treats that as a downgrade, which is why it needs --allow-downgrades:

sudo apt purge $(dpkg-query -W -f='${Package}\n' 'linux-*zabbly*')
sudo rm /etc/apt/sources.list.d/zabbly-kernel-stable.sources /etc/apt/keyrings/zabbly.asc
sudo apt update
sudo apt install --allow-downgrades "linux-libc-dev/$(. /etc/os-release && echo "$VERSION_CODENAME")-updates"

A kernel you built yourself is the easiest of the three to remove, because bindeb-pkg makes just two packages you installed and nothing pulls them back in. After the grub-reboot into the stock kernel, purge both by name:

sudo apt purge linux-image-7.3.0-custom linux-headers-7.3.0-custom

The purge freed 778 MB on 26.04, regenerated grub.cfg without the 7.3 entries, and took /boot from 218 MB back to the 151 MB it used before the install. The only noise is dpkg: warning: while removing linux-image-7.3.0-custom, directory '/lib/modules' not empty so not removed, which is about the shared parent directory and is harmless. The packages are not the big cost, though. The source tree still holds about 42 GB, so once you are sure you won’t rebuild, delete it as well:

rm -rf ~/kernel-build

That directory also holds the four .deb files the build produced, the 1.5 GB debug image among them. If you want to install the same kernel on another machine later, copy the image and headers packages somewhere else before you delete it.

Fix “bad shim signature” after the reboot

This is what Secure Boot does to every kernel in this guide. We captured it from the serial console of the UEFI 24.04 VM, booting the unsigned mainline image with Secure Boot on:

error: prohibited by secure boot policy.
error: bad shim signature.
error: you need to load the kernel first.
Press any key to continue...
Failed to boot both default and fallback entries.
Press any key to continue...

GRUB does not fall back to the old kernel on its own. It stops at the menu and waits for a keypress, so a remote server with no console access stays down until someone reaches it. From the console, open “Advanced options for Ubuntu” and boot the stock kernel, which is signed and still works. Restarted with Secure Boot off, the same VM booted the mainline kernel, so the choice is about the firmware setting, not the kernel. You have three ways forward:

  • disable Secure Boot in the firmware or instance settings and keep the new kernel
  • remove the new kernel as shown in the removal section
  • sign the kernel with your own Machine Owner Key

Signing sounds like the clean answer, but it means enrolling a key on every machine and re-signing every build, which is rarely worth it for a kernel you are testing. For a test box, turn Secure Boot off; for anything that must keep it, stay on Ubuntu’s signed kernel.

For a machine you depend on, add Zabbly and let apt keep the kernel current. It reaches each new series with the first bugfix release, and on 24.04 it is the only prebuilt route that installs without patching anything. Build from source when you need a release today or a config option Ubuntu leaves off, and budget an hour and a half and 45 GB of disk for it. Leave the kernel.ubuntu.com packages alone until the builder publishes 7.x again, and on 24.04 even after that, unless you are willing to carry the two fixes above. If all you want on 24.04 is a newer supported kernel, upgrading to Ubuntu 26.04 gets you the 7.0 series with Canonical’s signing and security updates.

Keep reading

Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) Ubuntu Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Ubuntu 26.04 vs 24.04 LTS: Should You Upgrade Now? Ubuntu Ubuntu 26.04 vs 24.04 LTS: Should You Upgrade Now? Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Install Dagger on Ubuntu 24.04 / 22.04 Automation Install Dagger on Ubuntu 24.04 / 22.04

0 thoughts on “Install or Update the Latest Linux Kernel on Ubuntu 26.04 / 24.04”

  1. Mainline currently does not have amd65 .deb packages. So this won’t work for most people until the mainline kernel for amd64 is released.

    Reply
  2. excellent, I am finally able to get sound from my laptop speaker after installing latest kernel 6.10.6 on my hp envy17 on latest ubuntu cinnamon version

    Reply
  3. installed one latest kernel but still issues like

    WARNING: turbostat not found for kernel 6.10.7-061007

    You may need to install the following packages for this specific kernel:
    linux-tools-6.10.7-061007-generic
    linux-cloud-tools-6.10.7-061007-generic

    E: Unable to locate package linux-tools-6.10.7-061007-generic
    E: Couldn’t find any package by glob ‘linux-tools-6.10.7-061007-generic’

    Reply

Leave a Comment

Press ESC to close