Jeff Mixon / Why I run a self-built Linux kernel on my daily driver

Created Wed, 12 Nov 2025 00:00:00 -0800 Modified Mon, 22 Jun 2026 16:04:57 -0700

Why bother

apt install linux-image-amd64 is fine. It will be fine for most people forever. I do not run it on my workstation. The reason is small and concrete: the audio hardware on modern x86 laptops and motherboards is rapidly enough moving that the kernel my distribution ships when I install it is regularly not the kernel that knows how to drive the audio paths I actually have. By the time the fix reaches a stable release and is backported into the distribution kernel, six months have passed and I have not had a working microphone the whole time.

So I build my own. Not a once-a-decade adventure — the everyday kernel I boot into. Currently linux-image-6.18.34-x64v3-xanmod1-audio, with matching linux-headers and linux-libc-dev packages installed side by side. The build flow is the part worth talking about; the audio-stack work the build flow exists to enable is the more interesting part.

The build flow

The starting point is a cloned XanMod tree — a maintained patch stack atop mainline that gives me lower-latency scheduler defaults and a sane base for desktop workloads. On top of that I lay the audio patches I care about — codec quirks, smart-amp driver fixes, ALSA UCM2 profiles, ACPI subsystem-ID matchings — that have not yet landed upstream or have not yet propagated to a release.

A few choices that aren’t accidental:

  • A hand-crafted EXTRAVERSION and a distinct release codename. The installed package name encodes the patch stack (xanmod1-audio), and uname -r tells me at a glance what is actually running. Kernel debugging is hard enough without ambiguity about which kernel you are debugging.
  • x64v3 ISA targeting. Compile for the actual instruction set on the machine and let the optimizer use it. The portability cost is zero — this kernel only ever runs on this hardware.
  • Full Debian packaging. Not an out-of-tree blob, not a manually-copied bzImage. A proper .deb with a real changelog, control, files, rules, and substvars — produced by the standard build flow, installable and removable through dpkg like anything else. The linux-headers and linux-libc-dev packages are built and installed alongside the image so anything else on the system that wants to compile against this kernel (out-of-tree Wi-Fi drivers, virtualbox modules, the V4L2 loopback module) finds the headers where it expects them.

I keep multiple trees checked out — 6.8, 6.11, the Rockchip 6.6 vendor tree for SBC work, the XanMod tree for the daily driver — so when something breaks I can bisect across the relevant one without reconstructing the environment from scratch.

What the audio patches actually are

This is the half that justifies the build pipeline. Modern x86 audio is no longer a single chip with a single driver — it is a stack:

  • Sound Open Firmware (SOF) is the firmware running on the audio DSP, with its own topology files describing pipelines, mixers, and endpoints.
  • ALSA Use Case Manager (UCM2) profiles tell userspace how to route audio for a given hardware configuration (which jack is the headset, what to do when the speakers are routed through a separate amplifier, etc).
  • HDA codec quirks match specific machine models to specific pin configurations and verb sequences so the codec actually wakes up correctly.
  • I²C smart-amp drivers like snd_soc_aw88399 drive standalone amplifier chips that sit between the DSP and the speakers, with their own firmware and their own calibration.
  • DMI/ACPI subsystem-ID quirks are the glue: how does the kernel know which machine it is on, and therefore which of the above pieces apply?

A new laptop landing without working audio is, almost always, one or more of these layers missing a quirk or a topology entry for that specific machine’s identifier. The fix is the same shape every time — find the identifier, write the quirk, route it through the right subsystem — and upstreaming it is the actual contribution; running the kernel that includes it is just a daily-driver detail.

The reason I run the kernel I built rather than waiting is that I am the person closing the loop. I write the quirk, build the kernel with the quirk, boot it, confirm audio works, submit the patch upstream, and keep running the local build until the upstream patch lands in a release. Without the build pipeline that whole loop is gated on someone else’s release cadence.

Userspace audio too

The kernel work has a userspace mirror: a locally-built qpwgraph for visual PipeWire graph inspection (Qt6 / C++20 / CMake) and a tracked PipeWire configuration the patches were tuned for. The point is the same: if you are debugging audio plumbing, you want the graph tool that matches the graph you are debugging, not whatever version your distribution shipped six months ago.

Closer

The whole flow — kernel patch → personal kernel package → daily driver → upstream patch — is mundane in any given iteration. The compounding effect is that “audio doesn’t work on Linux on my hardware” is a problem I have been causally responsible for solving on my own machine for years, and the same skills make the upstream-contribution side cheap. There is no mystery to the Linux audio stack; there is just a stack of layers each of which rewards someone willing to read its actual source.