How to build a custom kernel for WSL in 2025
Thanks to the WSL System Distro, we no longer need distro-specific instructions to build a WSL custom kernel. A standard, ephemeral build environment is right there, on every WSL installation.
Previously, I have shown you how to build a custom kernel for WSL on openSUSE and for KVM acceleration.
Thanks to the introduction of the WSL System Distro, a lightweight Linux distro that runs on top of WSL managing various tasks, it is no longer necessary to write distro-specific guides to building a custom WSL kernel.
Because the WSL System Distro is standardized across all WSL installs, we can build a custom kernel for WSL the same way on any WSL install, regardless of your day-to-day WSL distro, whether it's AlmaLinux, Pengwin, Ubuntu, openSUSE, or Arch.
The WSL System Distro is powered by Azure Linux, also known as CBL-Mariner. The System Distro image ships with WSL, though it is mostly invisible. Unlike your day-to-day WSL distro though, the System Distro image is immutable. That means, even if you make changes in the System Distro, like installing packages or creating files, the minute you exit the System Distro, they are lost, and it reverts to the original version that the WSL team ships.
On the other hand, the WSL System Distro is the same across all WSL installations. Azure Linux is an rpm-based distribution that utilizes tdnf
, a simplified version of dnf
, the rpm-based package manager. tdnf
can connect to the Azure Linux Microsoft package repository and download any dependencies you need, including all the Linux build dependencies. We just need to build the kernel and export it to our Windows file system before it closes and all is lost.
Here is how to do that and keep that System Distro environment alive in the process.
Requirements
- WSL 2 installed with
wsl.exe --install
- Update WSL with
wsl.exe --update
- Any WSL distro installed, after all, you have to have a distro to boot into with your custom kernel
Entering the WSL System Distro
Getting root access to the WSL System Distro is as simple as:
wsl.exe --system --user root
Note without --user root
you will enter as the lower-privileged wslg user, which is utilized for WSLg GUI rendering, one of the aspects of the WSL experience the System Distro helps manage for WSL.
Note of Caution
Because the System Distro is immutable, if for any reason you exit the System Distro, WSL is accidentally shut down, or maybe your computer sleeps while your kernel build is happening, all your progress will be lost, and you will have to start over.
To keep the System Distro instance you are working on for your build alive, without losing any progress, I recommend two things:
- Download Microsoft PowerToys, enable Awake, and set it to keep your system awake indefinitely:
- Then, open a second tab in Windows Terminal, enter the System Distro as above with:
wsl.exe --system --user root
And then start a long sleep loop while we are working in the other tab:
while true; do sleep 10000; done
Installing Dependencies
Then we need to install the build dependencies for the Linux kernel in the WSL System Distro using tdnf
:
tdnf install -y gcc glibc-devel kernel-headers make gawk tar bc perl python3 bison flex dwarves binutils diffutils elfutils-libelf-devel zlib-devel openssl-devel ncurses-devel curl
Just a reminder, when you exit the WSL System Distro instance, all these dependencies will be gone, and it will reset to the factory image shipped by the WSL team, and you will need to reinstall these.
Kernel Sources
Move to your local home folder inside the WSL file system to improve build performance:
cd ~
Grab the tar build of the latest build of the WSL sources from GitHub:
curl -k -s https://api.github.com/repos/microsoft/WSL2-Linux-Kernel/releases/latest | \
grep '"tarball_url":' | \
cut -d '"' -f 4 | \
xargs -n 1 -I {} sh -c 'curl -k -L -o $(basename {} .tar.gz).tar.gz {}'
Unpack the tar archive:
tar -xzf ~/linux-msft-wsl-*.tar.gz
Building Your Kernel
Drop into the kernel sources directory:
cd microsoft-WSL2-Linux-Kernel*
You will need to use a kernel config file. The default WSL2 kernel config files are in ./Microsoft
:
You can manually edit these here or copy them and make your edits there and then point to them using KCONFIG_CONFIG=
when running make
on your kernel build.
I personally prefer to take the respective default WSL2 kernel config file for my architecture and copy it to .config
in my main kernel sources folder, which doesn't require setting KCONFIG_CONFIG=
.
For this Arm device, that would be:
cp Microsoft/config-wsl-arm64 .config
On x86 devices, that would be:
cp Microsoft/config-wsl .config
Customizing Your Kernel
If you copied the default WSL2 kernel config file to .config
, you can now edit using your preferred text editor, such as nano
, vim
, or emacs
.
My personal preference, usually when turning features on and off, is simply to use the TUI interface for customizing the kernel, which can be accessed with:
make menuconfig
Gets you to here:
Where you can easily enable and disable features, drivers, optimizations, etc., without having to decipher .config.
Save your config file (unless you want to save elsewhere and set KCONFIG_CONFIG=
on your kernel make
command) as .config
:
You can also apply patches, if you are advanced kernel hacker.
Building Your Kernel
It's now time to build your custom kernel.
Build with make
, specifying the maximum number of cores in your system to speed things along:
make -j$(nproc)
While it's building, just to be sure, make sure that sleep loop is still running, just in case:
Make you want to open another tab, drop into the System Distro, install htop
, and see all the magic happening:
tdnf install htop -y
htop
Installing Your Custom Kernel
Once our build is complete:
We copy our build into place:
On Arm devices:
cp arch/arm64/boot/Image $(wslpath "$(cmd.exe /c "echo %USERPROFILE%" | tr -d '\r')")
On x86 devices:
cp arch/x86/boot/bzImage $(wslpath "$(cmd.exe /c "echo %USERPROFILE%" | tr -d '\r')")
And configure our .wslconfig
to point to the custom kernel, if we are not replacing and existing kernel. In which case, you will want to make sure the configuration matches.
On Arm devices:
powershell.exe /C 'Write-Output [wsl2]`nkernel=$env:USERPROFILE\Image | % {$_.replace("\","\\")} | Out-File $env:USERPROFILE\.wslconfig -encoding ASCII'
On x86 devices:
powershell.exe /C 'Write-Output [wsl2]`nkernel=$env:USERPROFILE\bzImage | % {$_.replace("\","\\")} | Out-File $env:USERPROFILE\.wslconfig -encoding ASCII'
Testing
Once confirming Image
or bzImage
are in place and .wslconfig
is properly set, we can shut down our WSL instance. Note, confirm everything we copied from the System Distro is in place before doing so because all changes (installed dependencies, kernel sources, and built kernel) will be lost.
wsl.exe --shutdown
Then restart WSL in the System Distro or your preferred working distro:
wsl.exe --system
And run:
uname -a
We should see our kernel with a timestamp of our build date and time:
Happy kernel hacking!