Managing boot entries without nix-env?

Recently, I’ve been trying to understand just what exactly is happening if I am doing nixos-rebuild switch.

The relevant source files are

My current understanding is that roughly the following happens during switch:

  • First, nix build is run to, roughly, create a ./result file system tree with everything that is a part of a new version of the system.
  • Then, ./result/bin/switch-to-configuration switch. This is beautiful: we are using the binary from the freshly build system, running under the old system, to assimilate it. That switch-to-configuration does two things (well, maybe it does more things, it’s fairer to say that I understand two things :slight_smile: ):
    • poke systemd to essentially live-reload the system, using the new stuff
    • run that bootloader-updating script to persist the new configuration in the bootloader

Now we are coming close to the actual question I have — my understanding is the source of truth for the bootloader script are the nix-env profiles. So, before running the script we should add a corresponding symlink under /nix/var/nix/profiles/ using nix-env -p /nix/var/nix/profiles/system --set ./result.

It seems to me that that’s basically the single place in my system where I use nix-env, as I don’t use profiles anywhere else, because I don’t like how stateful nix-env is. Is there some way to install the bootloader bypassing the profiles infrastruture? Perhaps using existing /boot/loader/entries and explicit CLI arguments to reconstruct the state?

2 Likes

This is a good summary of what happens!

In principle the activation script could not add a profile generation, and merely parse and update the boot loader entries, but that would be more stateful and brittle, not less. Also you’d lose the ability to garbage collect old generations, so I don’t really see a way to get out of that without re-thinking the versioning paradigm of profiles.

But that sounds like an interesting exercise: is there a way to have a more hermetic equivalent of profiles? Git would be much more suitable for versioning generations, but it’s also more involved to tie that into the Nix language code consumers would write, and would require everyone who wants to use that feature to have their code versioned by Git. Maybe you have a better idea that has a smoother on-ramp and still scales?

PS: Making Git a hard dependency for everything Nix is not impossible, but it would need a lot of community buy-in. From experience I’d expect the pre-condition for that to be able to provide compatible fallbacks, which would likely make all of that impractically expensive. But it could work out cheaply for a fork or a wrapper project.

4 Likes

The other reason why we need profiles is that they serve as garbage collection roots of previous generations. Without them nix-collect-garbage would remove your old systems and leave your boot entries pointing to things that do not exist.

1 Like

Sounds like to me like you want nix-env without --install, --uninstall and --upgrade subcommands. I wonder if it’s too late to split imperative package management from nix profile into a separate command (possibly even separate tool), and leave the profile command only for management of generation pointers.

2 Likes

Yeah, precisely! I don’t mind statefulness per-se: obviously something somewhere should hold the list of generations and the list of gc-roots.

What I do mind though (in theoretical, not in practical sense), is that the mechanism used to record that state is the same mechanism that is used for imperative package management.

You can do nix build --profile yourprofile to a build and a nix-env --set in one.

1 Like