Nix is good actually?

Nix has been one of those things that's sat on my "that seems really cool" list for almost a year now. I've actually tried it a few times, but always bounce off at the docs or at the thought of all the "work" I'm going to have to do.

Well, today, I was unmedicated & while trying to work on something else, my brain decided: "Hey I reckon we could try and do nix, but like, just dotfiles".

I don't know why I never thought of this, I always see big grand setups - or huge NixOS repos, but it makes sense when thinking about things like home-manager. So I just sorta tried to do it.

I do have to admit - the docs are still fucked. But with the help of an LLM, Some oomfs configs1, and a general idea of how things should work I ended up with a pretty simple & sane setup that I actually like and understand.

I really do not fuck with the idea of having Claude write my entire config for me, so I really mostly used it to point me in the right direction. Ever since I did up my own nvim config myself, I've very much been in the boat of "you should write your own, and understand your own dotfiles". When something inevitably breaks, you're reading someone else's code if you didn't write it. That adds even more overhead and mental energy when you're trying to debug.

I started super small, just getting git & my gpg key setup. Since I do that a lot and it's always kind of annoying. I started with a new repo and a basic flake, I want two machines - Work & Personal:

{
  description = "pfych dotfiles";

  inputs = {
    nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";

    home-manager = {
      type = "github";
      owner = "nix-community";
      repo = "home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    { nixpkgs, home-manager, ... }:
    {
      homeConfigurations = {
        "pfych@personal" = home-manager.lib.homeManagerConfiguration {
          pkgs = nixpkgs.legacyPackages.aarch64-darwin;
          modules = [
            ./home/shared
            ./home/personal.nix
          ];
        };

        "pfych@work" = home-manager.lib.homeManagerConfiguration {
          pkgs = nixpkgs.legacyPackages.aarch64-darwin;
          modules = [
            ./home/shared
            ./home/work.nix
          ];
        };
      };
    };
}

It looks like a lot, but it's really not - Nix has some funky syntax, but it's all functions all the way down. They look like this:

{ value = { input, ... }: { foo = "bar"; }; }

In a fucked up comparison, think of it like this js:

const value = ((input, ...rest) => ({foo: "bar"}))()

So this base file defines a few variables: description, inputs, and outputs.

inputs is where all our packages come from, I've just got standard nixpkgs and home manager. These get passed into outputs! In my outputs I then configure everything via home-manager with two separate setups for both personal and work use.

What's cool about this is I can switch between them really easily:

home-manager switch --flake ".#pfych@personal"
home-manager switch --flake ".#pfych@work"

and everything flips over like nothing happened!

The actual home configs themselves are quite simple too, I point nix to the right packages and then import modules - these are just separate files super similar to this one! Let's look at personal.nix:

{ pkgs, ... }:
{
  home.username = "pfych";
  home.homeDirectory = "/Users/pfych";

  services.gpg-agent.pinentry.package = pkgs.pinentry_mac;
}

It's a function, that takes in packages (that we don't actually use 💀), and then it sets some variables that are system dependant. Like usernames, home directories, etc. Ideally I'd just have one darwin.nix and linux.nix but my Work setup is slightly different so it won't be that simple ;-;;

The more interesting file is ./shared! This is where the main config is! Shared is actually a folder containing a few files: git.nix, gpg.nix and finally default.nix - when you import a folder as a module nix looks for default.nix to import.

Here's what these files look like:

{ pkgs, ... }:
{
  home.stateVersion = "24.11";
  programs.home-manager.enable = true;

  imports = [
    ./git.nix
    ./gpg.nix
  ];

  home.packages = with pkgs; [
    gnupg
  ];
}

The default.nix first enables home manager for everyone and sets a stateVersion. I then import my git & gpg config, and then install gnupg with home.packages. This part is neat since it actually installs gnupg in my $PATH regardless of OS - so I don't need to use homebrew or pacman, etc.

My git.nix and gpg.nix then just have some basic config I'd like to share everywhere:

{ ... }:
{
  programs.git = {
    enable = true;

    settings = {
      user = {
        name = "pfych";
        email = "contact@pfy.ch";
      };

      init.defaultBranch = "main";
      commit.gpgSign = true;
      tag.gpgSign = true;
      push.autoSetupRemote = true;
    };

    signing = {
      key = "347543D390119D28232AA5D64CC16AAFFB23D192";
      format = "openpgp";
    };
  };
}

and

{ ... }:
{
  services.gpg-agent = {
    enable = true;
    enableSshSupport = true;
    sshKeys = [
      "D2785A4719E88BFEBB1F713B1E83E7CF9AB92225"
    ];
  };
}

This is really cool! So far it basically is just mirroring my existing yadm dotfile repo but using nix gives me a nicer lsp, typechecking and other goodies when working with all different applications configs!

The last little bit with nix that always confused me was "Where the fuck does all this config go?" - "Everyone shares cool repos, but when I set up xyz everything was in /etc??". Well, it turns out I'm kinda dumb - the answer is it doesn't really matter!

I've just got the repo set up in my home folder as ~/nix, but I could just as easily move it to ~/.config/nix or ~/Developer/nix if I wanted to - It'd just change the commands to build a little:

# Assuming I'm at ~ and my config is in ~/Developer/nix
home-manager switch --flake "./Developer/nix#pfych@personal"

I'm glad I pushed a little and actually tried to get something working this time. I'm going to slowly start moving my existing dotfiles repo over to my nix repo whenever I have some downtime. Just like my dotfiles repo, I'm content once it's set up and I don't find myself fiddling with them, & I'm hoping the same thing extends to nix!

So far I've managed to move over my nvim config & my Firefox config! Firefox was the coolest since I have all my chrome, plugins and settings sync'd now with nix instead of doing weird symlink stuff with yadm!

Footnotes

  1. 💖 Isabel & Olaren


This page was originally published 26 Mar 2026