Diffing NixOS configurations at the config-level

Hi folks,

for a looong time I wanted to have a way to diff two NixOS configurations at the config-level. I frequently thought about what options would be influenced by making a change to my configuration, for example when trying to bump system.stateVersion or enabling a new service. Today I am happy to announce that this is now possible.

TL;DR:


So while this turned out to be not-so-easy™, I finally managed to get it to work properly. It requires a small patch to nix’s evaluation logic, plus some minor changes to the NixOS module system evaluation to use the new primitives. It was important to me that this requires no modifications to any NixOS module, so it works out of the box with any NixOS configuration.

So I’m happy to introduce nixos-config, a utility that allows browsing and diffing configurations in the terminal. The required patches for nix and nixpkgs are contained in the repository and I am also maintaining a branch called thunk-origins-v1 in both my nix and nixpkgs fork with the patches applied. To test this you basically only have to enter a nix shell with the patched nix CLI and add a single line to your configuration to add the tracking information. Instructions are in the repository. I’ve also written a blog post that explains all of this in much more detail.

The terminal utility looks like this:

And here is a graph showing how options of a minimal NixOS configuration depend on each other (only showing options that were actually evaluated):

Of course there’s the awesome nix-diff tool already, but while it can explain what changes in the actual result derivatoin, it cannot tell me why. And this information is currently not accessible in nix, so this required writing some patches.

Oh and before I forget, this is experimental. (i.e. should work with any configuration, but no guarantees). I don’t have much experience with the Nix codebase, and I can’t say with certainty that the patches are not accidentally doing something that you shouldn’t do. If someone experienced could have a look at them, I’d be very glad! Also there are certainly edge cases that I missed, please tell me if you encounter anything odd.

75 Likes

Hello,

Cool thing, I hope I will try it soon !

Question: Do you plan to propose your patches upstream ?

Thanks

3 Likes

I’ll have to run it by some more experienced Nix people first to see whether it has any unintended side effects on performance. Also it probably needs a c++ cleanup… But generally I’d be happy to upstream it if there’s interest for it.

10 Likes

Man, that’s a brilliantly useful idea and allows to peak behind the curtains. Would be tremendously useful for debugging as well and a bit more straight forward then evaluating in the repl.

For example, currently it is not immediately obvious that immich or for example nextcloud enables other services without browsing through the code. Feels a bit like magic sometimes.

Thanks for experimenting with this! I’d love to have something like this upstreamed.

5 Likes

Looks insane. Could easily be the nix tool of the year.

The only problem is that one has to rewrite the flake in order to use it. This is a blocker as this prevents me from quickly using that tool on a config.

Can you make it work with any existing flake? Maybe you can use or upstream an extension mechanism similar to extendModules that allows you to reevaluate any given config with your modified module system.

5 Likes

This is probably the best thing I’ve seen released here. I cannot count how many times I’ve wanted to see config changes between big updates to the nixos-unstable channel, but simply can’t since it’s just not readily possible. The various diff programs are nice to see how the final derivation changes, but thats not necessarily always what I want to see.

Honestly I hope whatever you’ve done lands upstream for both Nix and Nixpkgs. This is such an invaluable contribution because it reduces the hidden obscurity of how the module system works, and makes things so much easier to trace and debug. I’m hoping to try this myself actually, hopefully soon!

5 Likes

I guess I could make trackDependencies = true the default in the fork, if that’s what you meant. But as far as I can tell, you will need to replace nixpkgs with the fork in your inputs (or by nix flake lock --override-input nixpkgs ...). Correct me if I’m wrong, but I believe these extension mechanisms would be run too late in the chain

Incredible, I’ve wanted this for a long time to make major NixOS releases less scary, hope this can be upstreamed!

5 Likes

Wow, this is awesome! I tried using this tool to diff facter configs and it works amazing!

3 Likes

Me not having to edit any nix in order to use the tool is what I mean.

You could upstream a change to nixpkgs that simply exposes all information which is needed for your tool.

As far as I understand, what you need is:

  1. The list of modules which were evaluated
  2. The source of the nixpkgs used, so you can apply your patches and re-eval the list of modules

If you can get these two things from any given evaluation result, your tool can be used on any given module system result. Nix might end up evaluating things twice, but I think this is an acceptable drawback given the much better UX gained by that.

This is available.