Why is Nix/home-manager so slow?
Sorry if this is an ignorant question. I love Nix, but as a user it feels absurd that changing an option from true to false in a home-manager managed config file that then gets transpiled from Nix to TOML, for instance, takes like 10 seconds on a decently modern machine.
In my world that kind of transpilation should be instant. I get that there's more happening behind the scenes than just doing Nix -> TOML, but still. Imagine if the transpilation of config files were instant, so you could have home-manager automagically do the transpilation every time you save the file. That would be awesome, especially for programs that support hot-reloading like Hyprland or Niri.
Is this a Nix issue or a home-manager issue? How much of speedup would it yield to rewrite Nix and/or home-manager in a faster language like Zig or Rust?
CppNix isn't slow because of C++, it's slow because its evaluation strategy (tree-walking an AST without any optimizations) is slow. Rewriting in Zig or Rust will not necessarily help; the structure of the compiler needs to be better. That said, Tvix is a Rust rewrite which uses a bytecode interpreter, and it is faster, but it's not a drop-in replacement like Lix. I prototyped a faster evaluator in RPython; see this LixCon 2026 video for my summarized notes to the Lix community.
Nix store transactions will never be instant. You've probably never thought about atomicity and durability for your configuration-file edits, but it's a desirable thing, right? Nix writes to a SQLite database for every transaction. This is not going to be a big part of your runtime as long as CppNix is so slow at evaluation, but it's the reason why trivial builds, like your derivations that merely copy text to the Nix store, take so long.
To add to the other answers, when I'm experimenting with some configuration a lot, I sometimes change it from being Nix-managed to just plain config file. That way I can try things out in a much shorter loop. When I have the configuration that I want, I port it back to Nix. Not a perfect solution, but it can save some time
Well, as you've guessed, it's not really just a transpilation. A NixOS rebuild evaluates significant parts of Nixpkgs,
<nixpkgs/nixos>as well as your configuration from scratch (technically flakes have evaluation caching, but it's not fit for purpose).Nix isn't a fast language to begin with, but the
evalModulescall that actually takes your configuration and turns it into a package that can be built is very expensive. Because NixOS imports (almost) all modules by default, it has to do deep merges of most of<nixpkgs/nixos>, which amounts to almost 32 MB of Nix code. Everything else is a rounding error compared to that.Oh, and it has to do this for Home Manager too, because Home Manager is also built on
evalModulesand also imports all the modules. As isflake-partsif you use that (though in that case the set of modules is small so you it's much less noticeable).It would probably make the performance worse before making it better. Nix is slow but it's not because of the implementation language (which is C++), you would have to do actual interpreter engineering (e.g. writing a JIT) to make an interpreter much faster than the current one.
A builtin (C++ instead of Nix) implementation of
evalModuleswould probably help, but nobody wants the versioning headaches that would come from that. I'm also not sure it would do that much, the amount of data is just massive.A more promising optimization is to make NixOS not import all the modules by default, but efforts to do this haven't really gained traction. Probably because you pretty much have to check all modules for cross dependencies.
As a user the best thing you can do right now is using Home Manager standalone and not as a NixOS module. That way, while switching generations will still be slower than it really should be, at least it won't evaluate and merge all NixOS modules every time you change your mind about a background color.
You can also use a non-Nix-based dotfile manager and forego Nix evaluation (and building) entirely. The only thing you really lose is being able to remote deploy dotfiles with things like
nixos-rebuild build-vm.Personally, I feel that over time this becomes less of a problem. It was very annoying for me as well, but as you get used to the language and the ecosystem, you get better at it and you will need less rebuilds to incorporate something into your config. And if it's only every couple of days, then I am completely fine with waiting 10-30 seconds for the rebuild.
If I'm trying to brute-force my way through something I usually continue to look for the next possible solution while the system is rebuilding, so it doesn't feel like time-loss to me.
@[email protected]
Home-manager has a large number of interdependent modules, which makes Nix's evaluation slow. If you add more external homeModules, the situation becomes even worse.
If you are using home-manager's nixosModule, you can try switching to standalone home-manager. Alternatively, you can try lightweight
$HOMEmanagement tools like hejm.You can also try home-manager's
lib.file.mkOutOfStoreSymlinkto speed up your dotfiles iteration. Note that if you are using flakes, you should use absolute path.