Compile a custom Linux kernel on Ubuntu
Compiling a custom Linux kernel on Ubuntu lets you enable an experimental driver, remove unused features, or test a patch before it reaches a distribution. It is useful when the installed kernel lacks support for newer processors, storage controllers, or peripherals. For ordinary desktop use, Ubuntu’s maintained build remains the safer choice because it is tested with bootloaders, security mechanisms, firmware, and proprietary drivers.
A custom build does not turn Ubuntu into a different distribution; it replaces the Linux core while keeping most of the userland. The desktop, package manager, and applications remain familiar. What changes is the kernel image and its loadable modules, so a poor configuration can affect booting, wireless networking, graphics, suspend, or virtualisation.
Plan a rollback before changing anything. Keep the working Ubuntu kernel, record its release with uname -r, and make sure GRUB can still select it. Australian buyers often choose ASUS, Lenovo, Dell, HP, or Acer notebooks, and many retail Windows machines arrive with Secure Boot enabled. That affects how a locally compiled kernel and its modules must be signed.
The job also needs time, roughly 20–30 GB of free space, and patience during compilation. On a typical Australian NBN connection, downloading the source is usually the short part; compiling, packaging, and testing consume most of the work. The commands below use Ubuntu 24.04-era package names as an example rather than assuming every release uses the same kernel version.
Plan the build around your Ubuntu release
Use the Ubuntu source archive when your goal is a reproducible build that preserves distribution patches and configuration conventions. The linux-source-* packages contain Linux source plus Ubuntu’s patch set, making them a sensible starting point for a desktop kernel. Match the major version to the release you intend to run whenever possible.
Upstream Linux offers newer drivers and experiments, but it omits Ubuntu-specific fixes and may expose firmware, AppArmor, installer, or third-party module incompatibilities. Choose it when a required driver is unavailable in the distribution kernel and you are prepared to test the result carefully. A newer version is not automatically a better system.
Whichever route you take, work in a clean home directory and give the build a distinct local version. This prevents it from overwriting the stock image and makes rollback straightforward. For wider context on Ubuntu releases, desktop tuning, and community projects, browse UbuntuNext community coverage before deciding how much you need to customise.
Prepare the source tree and tools
Install the build toolchain first. The build-dep command resolves the larger packaging dependencies, while the explicit list covers common compiler needs. Run these commands in a terminal:
sudo apt update
sudo apt build-dep linux
sudo apt install build-essential bc bison flex libssl-dev \
libelf-dev dwarves debhelper fakeroot dpkg-dev rsync kmod xz-utils
Use the package search to discover which source versions your Ubuntu repositories provide. This example installs the 6.8 source package; replace the digits if your release offers another version. After extraction, enter the actual source directory shown by ls if its name differs from the example.
apt-cache search '^linux-source-[0-9]'
sudo apt install linux-source-6.8
mkdir -p "$HOME/kernel-build"
cd "$HOME/kernel-build"
tar -xf /usr/src/linux-source-6.8.tar.xz
cd linux-source-6.8/
If upstream is required, obtain an official release tarball or Git checkout and use that directory for the remaining commands. Do not copy an x86-64 .config onto an ARM build: many Australian ARM devices, including selected Snapdragon-based computers and tablets, need make ARCH=arm64 defconfig or a configuration supplied by the device vendor. Confirm the architecture with uname -m first.
Configure drivers, features, and identity
Begin with the current kernel configuration to retain support for installed hardware. If the file is available, copy it into the source tree and let Kbuild add defaults for any new options. If /boot/config-* is missing, perhaps because old packages were purged, use a suitable architecture default instead.
cp /boot/config-"$(uname -r)" .config
make olddefconfig
Give the image a unique identity so it can coexist with Ubuntu’s kernel. The local version is included in uname -r, package filenames, module paths, and GRUB entries. Use menuconfig to review filesystem, storage, networking, graphics, and virtualisation options; hardware drivers marked as modules keep the main image smaller, while essential boot drivers are often built in.
scripts/config --set-str LOCALVERSION "-mykernel"
scripts/config --disable LOCALVERSION_AUTO
make olddefconfig
make menuconfig
grep -E 'CONFIG_LOCALVERSION|CONFIG_LOCALVERSION_AUTO' .config
Apply any patch before the final configuration pass, then run olddefconfig again. If a newer upstream tree reports missing certificate paths carried over from an Ubuntu configuration, use either its native defaults or the Ubuntu source package rather than weakening signing controls blindly. With Secure Boot enabled, follow a bootloader-compatible signing procedure for both the kernel image and its modules; an unsigned build may install but fail during startup. Disabling Secure Boot can also trigger Windows BitLocker recovery on some notebooks.
Compile the kernel and package it
Check available disk space, then run Kbuild as your normal user. Using every logical processor is usually fastest, although parallel compiler and linker jobs can exhaust a memory-constrained laptop. Builds should run while connected to mains power, with the machine resting on a hard surface and its vents unobstructed.
make -j"$(nproc)" bindeb-pkg
For an 8 GB or 16 GB system that runs out of memory, reduce the job count to two or one fewer than the processor total. Do not run make with sudo, since that can create root-owned files throughout the source tree. The command places Debian packages in the parent directory, allowing Ubuntu’s package manager to record the image, modules, headers, and build information consistently.
A modern desktop may finish in roughly twenty minutes, while a thin laptop can take several hours. During a hot Sydney, Melbourne, or Brisbane summer, ambient heat and internal throttling can extend that time further. If the build fails near certificate generation, revisit the Secure Boot configuration rather than disabling every signature check. Otherwise, record the exact error and correct its cause before compiling again.
Install, test, and preserve the fallback
Before installation, confirm that the stock Ubuntu kernel still boots and that its GRUB entry remains available. Back up important data and keep any Microsoft BitLocker recovery key accessible. Kernel images, modules, and initramfs files consume space under /boot, which may be a separate partition on some computers.
uname -r
df -h / /boot
dpkg --list 'linux-image*'
Move to the parent build directory and inspect the generated files. Install the packages belonging to this build, adjusting the filename patterns if necessary. A debug-information package can be exceptionally large and is unnecessary for a basic boot test. After installation, regenerate the boot menu so the custom release is visible.
cd ..
ls -lh *mykernel*.deb
sudo apt install ./linux-image*mykernel*.deb \
./linux-modules*mykernel*.deb \
./linux-buildinfo*mykernel*.deb \
./linux-headers*mykernel*.deb
sudo update-grub
Reboot and select the custom release from the GRUB advanced options before relying on it as the default. Once the desktop starts, verify the running release and check that expected modules and external drivers are present. NVIDIA, VirtualBox, and similar components may need recompilation against the matching custom kernel headers, and Secure Boot can prevent an unsigned module from loading.
uname -r
lsmod
dkms status
Keep the stock kernel through several successful boots and normal workload changes. Remove obsolete custom packages later through APT using their exact names, never by deleting files directly from /boot. When Ubuntu introduces a new kernel series, build the patch against that release rather than overwriting an existing image with the same release name.
A custom kernel is most useful as a controlled experiment with a measured purpose. Ubuntu’s distribution kernel, bootloader integration, and external driver ecosystem remain valuable even when a newer upstream build appears attractive.
What should remain constant is the method: start from a known configuration, identify the build clearly, preserve a working system, and verify the new kernel before retiring its fallback.