Skip to content

Accelerating Dotfiles Debugging ​

After managing our Dotfiles with Home Manager, one issue we may encounter is that each time we modify our Dotfiles, we need to run sudo nixos-rebuild switch (or home-manager switch if you are using Home Manager standalone) for the changes to take effect. However, running this command recalculates the entire system state each time, which is painful despite the many caching mechanisms already in place within Nix to accelerate this computation.

Taking my Neovim/Emacs configuration as an example, I make frequent modifications to them, sometimes dozens or even hundreds of times a day. Having to wait for nixos-rebuild to run for tens of seconds every time is a sheer waste of time.

Fortunately, Home Manager provides a mkOutOfStoreSymlink function, which creates a symlink to the absolute path of your Dotfiles. The content stays in your home directory instead of being copied into the Nix store, so your modifications take effect immediately.

This method is effective under the premise that your Dotfiles content is not generated by Nix. For instance, my Emacs/Neovim configurations are native and are only linked to the correct locations through Nix Home-Manager's home.file or xdg.configFile.

Below is a brief explanation of how to use this function to accelerate Dotfiles debugging.

Assuming you have placed your Neovim configuration under ~/nix-config/home/nvim, add the following code to your Home Manager configuration (e.g., ~/nix-config/home/default.nix):

nix
{ config, pkgs, ... }: let
  # path to your nvim config directory
  nvimPath = "${config.home.homeDirectory}/nix-config/home/nvim";
  # path to your doom emacs config directory
  doomPath = "${config.home.homeDirectory}/nix-config/home/doom";
in
{
  xdg.configFile."nvim".source = config.lib.file.mkOutOfStoreSymlink nvimPath;
  xdg.configFile."doom".source = config.lib.file.mkOutOfStoreSymlink doomPath;
  # other configurations
}

After modifying the configuration, run sudo nixos-rebuild switch (or home-manager switch if you are using Home Manager standalone) to apply the changes. From then on, any modifications you make to ~/nix-config/home/nvim or ~/nix-config/home/doom will be immediately observed by Neovim/Emacs.

This way, you can manage all your Dotfiles using a single nix-config repository, while frequently modified non-Nix configurations can take effect quickly, unaffected by Nix.

Notes

  • Pass the path as an absolute string, as in the example above. A path literal such as ./nvim is copied into the Nix store when the flake is evaluated, so the symlink would point at a read-only snapshot: your edits would no longer take effect, and the files would be owned by root/nixbld.
  • The linked content is outside the Nix store: it is not recorded in flake.lock and not covered by garbage collection. The symlink is still declared in Nix, so you have to provide the content yourself: make sure the files already exist at that absolute path, for example by cloning your dotfiles repository there. Evaluation stays pure (no --impure required).
  • The config used here must be the Home Manager module's config. If NixOS' config is in scope instead (for example when the user definition and the Home Manager configuration share a module), config.lib.file does not exist.
  • Don't point it at a path that a program module already manages. If programs.<name>.enable = true writes the same file, Home Manager reports a conflict; disable that module and manage the file yourself.
  • A linked directory becomes a single symlink, so it is recursive by nature (recursive = true is not needed), and everything inside it leaves Home Manager's control.