Home-manager is a false enlightenment

People thought this was kind of spicy on r/NixOS when I posted it – interested to see the reaction here :upside_down_face:

7 Likes

Nicely written, as always!

Pardon my ignorant question, but the crux of the article, at least for me, boils down to this:

When possible, you should strive to remove as many files symlinked into your home folder

Can you please elaborate more on why symlinks should be avoided in home folder, when, as a NixOS user, they’re all over the rest of the system?

Assuming I’m fine with having to involve Nix any time I want to modify any application config and the tradeoffs that follow, all other things being equal, what is so special about homedir so as to be preferred without symlinks?

3 Likes

I concede that all else being equal being preserving closure is a good thing, but I don’t find this compelling. If I want my setup on another computer I clone my repo, I don’t find myself wanting to copy the closure of random tools from one machine to another.

3 Likes

If you can generate a wrapper, you dont need home-manager, just use a wrapper from nixpkgs.

home-manager shines when you have dependant configs.

My favorite bit of home-manager is the email ecosystem: you define your email setup once and it can generate configurations for all your mailreaders, mail retreive systems, mail classification system and so on.

Some software need their password in cleartext in the config, which prevents me from putting them in a public dotfiles repo. With home-manager, I can generate those.

If you take neovim for instance, wrapping it changes its behavior so symlinking files in the usual location ~/.config/nvim helps users to stay in a more familiar setup.

And I mean anything that needs some logic can benefit from HM, it can generate/include different configs depending on the target mahcine (laptop vs desktop etc).

Drawback of HM is the need to regenerate the configs. But most software (neovim/waybar etc) support a hybrid setup where part of the config can be generated and the other imperatively handled.

2 Likes

I read this more as “wrapping > out-of-store symlinks”, in general, not just in the homedir. i.e. being able to run a binary directly is nicer than having said binary depend on a file being located in just-the-right-spot.

Though, as many programs do not support wrapping in this manner (i.e. they cannot be coerced to read their config from envvars or CLI flags), this requires patching upstream code (or is practically impossible in the case of proprietary programs).

And of course, if the config is also in the store and not symlinked out, you can’t mutate the config if you need to do so ad-hoc, and inspecting your overall config can get uglier.

And… not-using nix is a nonstarter for me, for the simple reason that I want programs to know about each other’s config (mediated by nixos modules). That was the whole point of using the module system, right? If we were just writing independent files then sure, nix is a waste of time, just chezmoi or stow or git clone and get on with it.

6 Likes

yes – the post is a bit of a lark (or purposefully inflammatory) but just trying to highlight that the vast use of home.file is a bit counter to Nix ethos especially if you COULD tie them… but there’s a level of pragmatism we all need.

For instance, I was discussing gitconfig with a friend. You could tie git + gitconfig in the same binary but then… maybe your IDE vscode also reads your ~/.config/git/confg. Now you have to fix vscode (or that plugin). From a purity standpoint, that is the right thing to do, since Nix showed us an implicit dependency from vscodegitconfig that was not modeled… but practically it’s definitely diminishing returns.

Nix is the world’s best yak-shave and exemplifies “boiling the ocean” – in the best possible way.

1 Like

I see it more as an alpaca shave.

Just here to self-plug my library wrapper-manager: https://github.com/viperML/wrapper-manager , as a module system abstraction over wrapProgram.

I’ve been using WM in my dotfiles for 90% of the programs I use, for already more than a year.

6 Likes

I’ve actually had to closure-copy my hm setup in anger (over an air gap), and running the hm activation script (which puts the symlinks in place) is no big deal compared to everything else involved

2 Likes

I like the post and think it accurately characterises a place where implicit dependencies can creep in.

I’m unclear what benefits arise from being strict on this, but the wider Nix ecosystem was built on the explicit dependencies, and we’ve seen the variety of ways that’s beneficial, so it would be interesting to see what comes from strictness.

One idea off the top of my head… a wrapper of binaries+config isn’t that far off a generalisation of ExecStart, and so could form the basis of better reuse between ExecStart, launchd, Dev shells, and containers (and dotfiles!).

It’s a shame that when you want to run a program in systemd, there’s often a great module, but if you want to run the program in another context, you start from zero.

E.g. a dev shell that wants to customise gitconfig has to figure out how to inject config. Why not just use gitconfig’s own profile mechanism? I prefer my modularity to live at the nix layer. Some tools don’t have a profile mechanism. Even if they do, I often can’t predict which context is global and which is per-profile and so unintended implicit dependencies change behaviour on me.

My favorite bit of home-manager is the email ecosystem: you define your email setup once and it can generate configurations for all your mailreaders, mail retreive systems, mail classification system and so on.

This is a great thing, but seems orthogonal to wrapper vs symlinked dotfile.

And of course, if the config is also in the store and not symlinked out, you can’t mutate the config if you need to do so ad-hoc, and inspecting your overall config can get uglier.

This applies to NIxOS in general, e.g. trying to find the unit file that’s backing a systemd unit.

3 Likes

From the post:

When possible, you should strive to remove as many files symlinked into your home folder.

Can the same be said for /etc? :slight_smile:

Consider the flexibility and challenge of rooting out the implicit dependencies on /etc/nsswitch.conf

1 Like

I’d say I probably will never use nix copy to move a small portion of my setup to some other machine since I already expose my editors (nixvim and nix-doom-emacs-unstraightened) and configured terminal emulator from my config

nix run --accept-flake-config .#attr would work perfectly and probably faster than nix copy P2P transfer since my cache is served behind Cloudflare

And I’ll never find a friend who don’t use Nix AND will let me install Nix on their precious machine

Your (any many others) problem with home-manager was non existent on my end since the very beginning

Correct me if I am wrong, as I don’t have the skills nor the insight as either the authors of the blogs.

But could it not be easier explained the issue by simply stating that:

Using home-file in home-manager to symlink configuration files, every symlink will be recreated every time a new generation of home-manager is generated. The symlink is then to a home-manager generation dependent, and even if you don’t change anything related, even the smallest change that creates a new generation, will delete previous symlinks and create new ones.

When you put the configuration into a package (and outside of being dependent on the home-manager generation), that symlink result will only be changed when it really needs to.

Either putting the configuration on the package derivation or creating a new package with the contents of the configuration, should at least solve this issue (and then maybe use activation stage to do the symlink, which is probably better, since it removes the 4 symlinks loop of home-manager “OutOfStoreSymlink”, bonus if your symlink script can detect if the file in place is exactly the same and just skip it)

Not agreeing or disagreeing, just asking if I am understand it correctly.

1 Like

YES!
I posted this on twitter (X) link.

In a crazier world there’s no need for even /etc right ?
Code that defaults to reading /etc like sshd could just be given their config also… All the way down to systemd. These are all pre-Nix concepts we can and should shed.

tl;dr; Even /etc/ can theoretically be removed if you wrapped every program to include the necessary configs it needs – this is the ultimate yak-shave but would be a perfect representation of all the dependencies on a system. (Like my vscode example before reading gitconfig)

2 Likes

That’s one thing but now you have two packages: the symlink and the binary that have this implicit dependency.

You might delete the Nix line that creates the home.file link, since it could be in any Nix file, and suddenly your binary stops working; that is exactly the type of bugs Nix as a whole is trying to solve.

By having the dependencies modeled in the /nix/store you can actually browse the graph of dependencies nix-store --query --graph.

The package and the config file using home.file are not represented as links in the graph.
They are however if you wrap them together in a wrapper program :slight_smile:

It feels like I’m splitting hairs but the difference is quite profound.

1 Like

Even exposing it as a package in your flake necessitates wrapping it.
If your package relied on home.file, exposing the binary wouldn’t give you the replicatibility you desire.

I was thinking more like: 2 packages, the binary and the config. The config package would have dependency on the binary.
The symlink could go into activation phase (could also link the config specific for host in use or have configuration options. I have many config files that are most similar between hosts, but differ in a little).

What I have done so far, is use home.file when I am lazy or want quick testing, or wrap a package when I am sure that the desired result is what I want. (granted, haven’t used wrapper manager yet, but been meaning to for a while. So far, I did my own libs to wrap packages, mainly because I wanted to learn how to do it on nix). I guess this is as enlightened as I can be :frowning:

My big issue with using activation phase linking, is that what I really must have is: I remove something from the configuration, it must be removed/reflected in the output (home folder).

yeah ur right im basically wrapping everything lmao

going this way basically means putting (wrapped) packages in home.pacakges instead of using modules that might depend on home.file

wrapping may be more pure, but I don’t really understand what’s so bad about certain files being in certain places (leaving aside the (greatly overblown) gotchas of maintaining link farms).
what is so great about having to peel off wrappers in order to examine textual configuration?
what is so great about inability to use find/grep in a well-known tree that may contain configuration pieces of which technically “belong” to different packages (like /etc or ~/.config)?
file systems organize things for human consumption, which is good actually.

1 Like

I don’t think it is a question of purity / referential transparency (assuming that’s what you mean by purity). To steel man the argument: if the joy of nix is that generally to refer to something is to automatically depend on it and to have that dependency tracked and enforced and consumed by tools, home.file by-passes this and drops you back into “place-oriented” programming land where special names have special meanings which are manually remembered and tracked. That is to say you can remove a home.file entry and degrade your system. If the configuration is referenced as store path in a wrapper and the configuration is removed, then evaluation will fail, and this is a good thing :tm: .

I don’t buy this argument. I think selective weakening has it’s benefits, as you point out. Some paths perhaps should distinguished, and it’s relatively easy to place nix expressions referencing binaries and their associated configuration next to each other so they are not easily confused or forgotten. All in all, having a “normal” home directory easily outweighs the loss of strict automatic dependency tracking for me.

1 Like