HomeManager and GNU Stow

I’m looking to use GNU Stow to symlink my dotfiles. I’ve read around that you can do this with HomeManager and have it write the configuration files out for you as describe in Managing dotfiles with Nix, but as mentioned in this thread by some participants this isn’t a viable solution for me because I use my dotfiles on other OS’s (Gentoo and Arch) and tweak them here and there and don’t want to have to mirror those changes to a HomeManger/NixOS configuration (I’m not ready to switch to NixOS for those systems (one is work where I need things to “Just Work™” and don’t have the time to tinker).

I read in this thread that HomeManager can be configured to point to my relevant dotfiles/* but this negates the convenience of using Stow to symlink to all the files.

What I’d like is to include stow in the user packages and then run a short script (nix-shell) to clone my dotfiles, move into the directory and run stow . and it sets up all the symlinks automagically and I’m good to go as over time I can modify my dotfiles across various systems and keep them in-sync with just cd ~/dotfiles && git pull.

  1. Does this make sense?
  2. Is there a way to configure NixOS to clone the dotfiles and use stow to setup the symlinks as required?

Thanks in advance,

slackline

Hi slackline,

I’ve used both HomeManager and GNU Stow to manage dotfiles. Like yourself I also use other distros so using HomeManager exclusively does not work for me. My first solution was to use HomeManager’s home.file function to point to my Stow dotfiles, so then when I run home-manager switch, the files are added to the Nix store and symlinked to my home directory.

home.file = {
    ".vimrc".source = ~/dotfiles/vim/.vimrc;
    ".bashrc".source = ~/dotfiles/bash/.bashrc;
};

I still made all my changes in the original Stow dotfiles directory but I had to run home-manager switch after every change. I was also unable to edit files using the symlink because the Nix store is read only.

Ultimately I decided to ditch this approach and now I just use Stow on NixOS the same way I do on every other distro. All I had to do was add the stow package to my configuration.

Why would home-manager not work in such a case? If anything, it is tailored especially well to such a usecase. You can reuse your home-manager configuration across multiple <nixos/home>Configurations, simply import home-manager as a NixOS submodule for NixOS systems, and run your home-manager configuration standalone on non-NixOS systems; distro-specific quirks can be handled by introducing custom configuration options. I do such a thing myself with great success.

1 Like

You can absolutely use home-manager like stow, ignoring any of its useful features, with the only difference being that you need to run a switch if you make a change.

This usually seems like a deal breaker for new users, but IME you stop changing stuff regularly enough that this matters after a little while. While you tinker it’s easy enough to point whatever you’re configuring directly at your repository.

IMO argue home-manager is much better than stow when you use multiple distros, since you can embed conditionals depending on which OS you’re on, that way you can actually use one config everywhere and aren’t carrying along a bunch of diverging branches. Stow only makes sense if all your computers are configured exactly the same.

1 Like

When you say you can embed conditionals depending on OS, do you mean it can tell the difference between x86-linux and Darwin, or can it differentiate between NixOS and Fedora? If the latter, then that could be a game changer for me.

Have you got an example we can look at?

Thanks @ensignnix , @Swarsel and @TLATER for your replies, all really useful.

I’ve already started using xdg.configFile = { ... } and home.file = { ... } constructs to get things moving.

I like the suggestion of using Nix and home-manager on other distributions as an alternative to stowand would also be interested to know how to embed conditionals (I already use them in my .zshrc based on ${HOST}/ ${HOSTNAME} rather than the hassle of diverging branches.

I agree @TLATER, these days I don’t tend to tweak configurations that often, the exception is my Emacs configuration which is under constant revision and improvement to the extent that it has its own repository for configuration outside of all my other .dotfiles :face_with_open_eyes_and_hand_over_mouth:

Deoending on what you need, you have for example pkgs.stdenv.isLinux or if you want more control over the architecture, simply pkgs.system.

For checking which distro you are one, I would suggest introducing an own home-manager option (eg. options.custom.distro) of type string that you can check against and perform config based on that. To elaborate, you could then use this with <name> = lib.mkIf (config.custom.distro == "fedora") <value> for example.

In theory it should also be possible to perform some magic on /etc/os-release at activation time, but that seems like a great hassle just for having an ‘automatic’ check.

1 Like

Enums are probably more appropriate, since you’ll have a finite number.

But yes, that’s the gist of it. Define separate entrypoints for each host, and eval the correct config for the correct distro - the conditionals you place in your files will make the changes to the config you needed for the specific distro.

Combine with flakes and have per-host entrypoints and you can get full auto “detection” by hostname!

It’s why I personally originally started using nix; at the time I was using many different computers, many not controlled entirely by me, and needed some small tweaks to some things per-host. Stow was not good at this. I don’t have an example anymore, unfortunately, that’s long ago and way too far back in git history to reasonably find :wink:

1 Like

If you’re interested, I’ve stolen a fun approach to emacs config from @matthewbauer - use use-package or leaf to define dependencies, and then parse the configuration to produce a list of packages for nix to include in the emacs closure.

That way you get neatly reproducible emacs packages, and you can use nix’ overrides and whatnot to make small changes e.g. in version, or patches for broken melpa packages where necessary. This is my emacs derivation: dotfiles/pkgs/applications/emacs/default.nix at 2ccdbf7ce777b6c146d88aa12fd973ded2e8ac9e · TLATER/dotfiles · GitHub

It does rely on import-from-derivation. You could run the package list generation in a separate command, but I don’t care enough about the small increase in switch time for my personal config.

I find emacs is well suited to the switch workflow, too, since you can just eval-expression whenever you make small tweaks to see what happens.

2 Likes

Interesting - this approach is quite similar to config in emacsWithPackagesFromUsePackage from the emacs-overlay, right? Or is there a functional difference? (for reference here is my config using it: .dotfiles/profiles/home/common/emacs.nix at 07a99987a784d95255154103864ac7488fcbdda1 · Swarsel/.dotfiles · GitHub)

I think this also does not avoid IFD, but I feel the same way about it.

1 Like

Oh my, that it is indeed. That one doesn’t use IFD, it’s a simplified .el parser written in nix!

My setup predates that, I suppose I should look into adding support for leaf and switching to the overlay :slight_smile:

@TLATER & @Swarsel : Thanks for those pointers on Emacs both really useful, going to take some time to digest it.

Returning to work soon so time to tinker will be reduced but enjoying moving along Nix learning curve. :chart_with_upwards_trend:

Thanks all for the really useful replies. :+1:

1 Like