Nixos: importing "extra" functionality

i know conditional imports aren’t good because they often end up in infinite recursion - there are a number of posts on this discourse telling people to create module options instead of trying to conditionally import functionality

so… why do we have so many nixos configuration files which need to be imported? profiles, qemu, lxd, perhaps even virtualbox config, as examples

should we be creating options for this functionality instead of using conditional imports? perhaps something like profiles.minimal.enable = true; instead of imports = [ "${modulesPath}/profiles/minimal.nix" ];?

i ask because i’m in a position where i want to start conditionally importing some of these files in a way that ends in an infinite recursion - if these extra files were modules guarded under options i wouldn’t have any problems


cc @Infinisil and @roberth :heart:


Same for nixos-hardware, by the way. I think nixos-hardware would actually significantly benefit from such a change internally as well, for all the reasons the NixOS module system exists in the first place, and it would make the flake support so much cleaner.


I believe performance (importing all modules by default slows down evaluation) and convenience (it takes more boilerplate to write enable options) are the main reason these modules don’t guard their configuration under an enable flag.

Though at this point, these couple modules probably wouldn’t even add that much evaluation time in relative terms, since there’s already like 1.5k modules included by default… Any proposal to improve the speed of NixOS modules would have to deal with all of those anyways.

And making all NixOS modules uniform would reduce a bunch of confusion and allow such conditional use cases.

So I’d be in favor of experimenting with making all those modules have an enable flag! (Of course only if we have a backwards compatible migration plan).


“Import only what you need” solves the performance problem by construction, but NixOS isn’t set up to support this yet (except for the rather experimental nixos.evalModules entrypoint, which doesn’t import basically anything from the modules in module-list.nix (those 1.5k @Infinisil mentions).

This idea is orthogonal to enable flags. It’s entirely possible to have optional modules (not unlike profiles) that don’t configure anything unless their enable flag is set to true.

Suppose we did both, we’d have roughly 10× fewer modules to evaluate, and some of the modules have unnecessary enable flags, so we don’t get an additional 10%(?) reduction in the number of options.
That seems pretty ok to me. The real question is how do we make it nice to use?
One thing we could do is refactor the profiles into something like:

  imports = [ ./foo-impl.nix ]; = true;

Where foo-impl.nix is what’s currently in the foo.nix profile, except its config is a mkIf cfg.enable { stuff }.

Now I don’t like profiles.minimal. The problem is that it still doesn’t import only what you need.
Being minimal is a responsibility of the entrypoint; not of some option that couples to all the “core” modules.
In fact, the way it should work is that using the minimal entrypoint becomes the norm, and everyone imports a profile named traditional to get the current behavior. That way they can change their config to a different profile, such as perhaps desktop or server, or they could import the bare minimum they need, if they feel like experimenting.

Adding minimality to modules that have already been released is going to be a somewhat of a problem regardless. Probably the best we can do is add alternative modules, or indeed something like profiles.minimal.enable (although that particular option isn’t widely applicable of course; it shouldn’t give me a minimal nginx module or anything like that).