Custom kernel and config
This entry covers how to build your NixOS VM using a custom/third-party copy of
the Linux kernel and a completely custom kernel config (.config
).
Note that this is a niche use-case, if you just want to enable support for
additional hardware, then consult the NixOS wiki for
custom configuration.
Warning
Again - if you don't know exactly why you need to do this, you won't have a need for it. The generic NixOS kernel and configuration is fine for most!
The result of what we want to build is something like this flake.
Initializing the project
You can start a new project using a template rather than re-creating the entire flake VM configuration from scratch:
Using a custom kernel
linuxKernel.manualConfig
is a helper function in nixpkgs
which builds a
Linux kernel given the source, a config file and (optionally) some patches.
Note that the version
and modDirVersion
values are required and used as
sanity-checks. version
just matches the version of the kernel you are using -
you can get the proper value by running make menuconfig
yourself.
modDirVersion
is just the concatenation of the version and the LOCALVERSION
value. LOCALVERSION
can be set to disambiguate multiple installed kernels. In
our case, the linux-qemu.config
we provide defines
CONFIG_LOCALVERSION=.nix4noobs
.
Finally, note that src
can be provided through other means, you can use a
different helper to download a tarball, or provide a local source directory.
# custom kernel, custom config
boot.kernelPackages = let
my-kernel-pkg = { stdenv, lib, linuxKernel , ...} @ args:
(linuxKernel.manualConfig rec {
inherit stdenv lib;
version = "6.3.0-rc4"; # from `make menuconfig`
modDirVersion = "${version}.nix4noobs"; # "${version}{CONFIG_LOCALVERSION}"
configfile = ./linux-qemu.config;
allowImportFromDerivation = true;
src = builtins.fetchGit {
url = "git@github.com:torvalds/linux.git";
ref = "refs/tags/v6.3-rc4";
rev = "197b6b60ae7bc51dd0814953c562833143b292aa";
};
kernelPatches = [];
});
my-kernel = pkgs.callPackage my-kernel-pkg {};
in
pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor my-kernel);
Note
allowImportFromDerivation = true
is important, it allows us to
just specify the configFile
value as a path which is then imported.
Note
You may wonder where the stdenv
, lib
and linuxKernel
attributes
come from, seeing as my-kernel-pkg
is called further down with an empty
attribute set.
This is one of the magical properties of callPackage
which, somehow,
provides any attribute from nixpkgs
/pkgs
, if requested:
See callPackage, a tool for the lazy.
Disabling modules check
If you built the VM as-is, you would likely run into failures toward the end of the build as the builder will attempt to copy various kernel modules from the compiled output. If any of these modules fail, the step will fail, causing the entire build to fail.
This check reflects the standard NixOS kernel configuration, which builds
modules for a multitude of things such as raid0, raid1 and so on.
These checks cause issues when building a highly stripped-down kernel, but we
can disable them by modifying the makeModulesClosure
function with an overlay:
nixpkgs.overlays = [
# (for custom kernel)
# avoid errors on missing modules such as md, raid0, raid1 etc.
(final: super: {
makeModulesClosure = x:
super.makeModulesClosure ( x // { allowMissing = true ;});
})
];
Overlays are covered elsewhere, but suffice it to say
that super
represents the state prior to this overlay being applied, and
final
represents the final state after all overlays are applied, and the
returned attribute set are the changes this overlay makes to nixpkgs
.
In this case, we redefine makeModulesClosure
, calling the original
implementation, but merging the input attribute set with one where the key
allowMissing
is set to true. This effectively skips this check.