json2dir

A JSON-to-directory converter, a fast alternative to home-manager for managing dotfiles

View on GitHub

Using json2dir as a home-manager alternative for managing dotfiles

You can use json2dir to generate your dotfiles and use nix profile to manage your packages.

The idea here is to use nix eval --json to generate the JSON representing your dotfiles (or, in general, parts of your home directory) and pass it to json2dir. Since the Nix builder is not involved, this is fast.

You may wish to use Nixsa or a similar solution to use Nix without root.

You don’t have to use this tool in conjunction with Nix! You can also use JSON directly, generate it from YAML or Cue, or do something different.

Getting started

Here’s a sample single-file flake.nix to get you started:

{
  inputs.json2dir.url = "github:alurm/json2dir";
  
  outputs =
    { nixpkgs, flake-utils, json2dir, ... }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        packages.default = pkgs.symlinkJoin {
          name = "my-profile";
          paths = with pkgs; [
            helix

            git
            jujutsu

            fish
            direnv

            # ...

            # Replace $path_to_this_flake with the path to the directory of the flake.
            (writeShellScriptBin "nix2home" ''
              cd ~ || exit 1
              nix eval $path_to_this_flake#home --json | ${json2dir.packages.${system}.default}/bin/json2dir || exit 1
            '')
          ];
        };
      }
    )
    // {
      home.".config" =
        let
          full-name = "John Doe";
          email = "john@example.com";
        in
        {
          git.config = ''
            [init]
            defaultBranch = main

            [user]
            name = ${full-name}
            email = ${email}
          '';

          jj.config = ''
            "$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json";

            [user]
            name = "${full-name}"
            email = "${email}"

            [ui]
            diff-editor = ":builtin"
            default-command = ["log", "-r", "all()"]
          '';

          direnv.direnvrc = ''
            source "$NIX_STATE_HOME/profile/share/nix-direnv/direnvrc";
          '';

          # ...
        };
    };
}

Note that if you want to try out this example, you need to update $path_to_this_flake to point to your flake.

To apply this configuration, you need to install the profile:

nix profile add .

Later, this profile can be upgraded with nix profile upgrade <name>, where <name> is the name of the profile you just added (you can discover it by running nix profile list).

After you have installed this profile (and added it to your PATH), now you can use the nix2home helper (defined via writeShellScriptBin above) to quickly update your dotfiles. Here’s an example run:

$ time nix2home
real	0m0.069s
user	0m0.060s
sys	0m0.009s

You might find nixpkgs.lib.generators useful for generating configuration files of a specific format.

More elaborate configs

Note that you don’t have to put everything inside of one big Nix file, of course. For config files requiring no templating, builtins.readFile (or some kind of wrapper around it) may be helpful.

Caveats

Old files will not be deleted automatically.

Files created by json2dir are mutable. This allows for avoiding Nix builds, therefore making updating files fast, and allows for quickly changing files temporarily, but is not declarative.

This solution doesn’t replace all of functionality of home-manager, only the dotfiles management part.