Notfiles: or how I learned to stop worrying and love Nix — Jacob Young
Skip to main content
Notfiles: or how I learned to stop worrying and love Nix
May 27, 2026
tl;dr: Checkout manager, my setup for a fully deterministic MacBook using Nix, nix-darwin, and home-manager.
After losing a MacBook Pro recently, 10 years of hand-written settings I depend on were gone. Backups existed, but all of the little configurations were lost to time. Things like macOS network settings, sleep/wake, time machine backup schedules, GUI applications and their configurations + licenses.
This motivated me to find a way to instantly recreate my MacBook in its entirety in less than 30 minutes without using backups or a self-hosted MDM profile. This includes GUI Apps, licenses, config files, system settings, launchd daemons, fonts and special files, etc.
I decided to embark on a quest to fold my entire machine into a single set of declarative configuration files. I had 173 packages across Homebrew and the App Store, macOS defaults, 196 system fonts, 5 Git identities managed by 1Password and GPG, custom zsh prompt, terminal, and editor config for vim, neovim, helix, and Zed.
Aside from losing a machine, I am regularly configuring new MacBooks for myself and my team. Most companies have a CISO and security policy which mandates external vendors use sanctioned machines (good policy if you ask me, waste of time if you ask me). I do a lot of Technical Due Diligence for M&A, which means time is money (literally). Any time I waste from opening the FedEx box to being maximally productive is an issue.
Nix has been on my radar for a long time. I often see blog posts about it and appreciate the sentiments. It wasn’t until LLMs started getting good that the idea (and time) of reproducing my entire development setup felt possible and worthwhile.
To quote Bill Baker on server management: “cattle, not pets”. This post is about moving a decade’s worth of accumulated dotfiles and ad-hoc macOS settings, apps, files, and more into a Nix-based machine definition as well as unlocking new levels of reproducibility.
dotfiles? more like notfiles
My first dotfile commit landed on August 3rd, 2015. It was a ~/.zshrc snippet that ran ls after every cd because I kept getting lost. Over the next ten years I effectively left the files on the machine’s disk. I might have had the idea to back them up to Github, but I certainly wasn’t thinking about making them robust.
Then Anish Athalye made a Python tool called dotbot which read a YAML manifest and symlinked tracked files into $HOME. My memory at the time was that there were 1-2 other tools doing something similar. devContainers were not popular or mature and devenv did not make it onto the scene.
While dotbot and tools like it are excellent it’s a bit of an intermediate tool between a simple Bash script and Ansible. It will place files where they need to go, and run arbitrary bash.
Dotfiles are not full-system configuration, just a few developer tools. It takes more work and effort to configure macOS System Settings, GUI applications, and 1Password. There’s only so far it will go before you either end up writing AppleScript to click on buttons or abandoning it. I ended up abandoning it for ~6 years.
Nix at a high level
I’m not a Nix person truly, but I admire it for being a powerful tool. This is my first foray into it, there’s a whole ocean out there, my experience is with dotfiles and config.
Nix is just a set of configuration files + a deterministic runtime which produces output. This makes Nix a package manager that treats every package install as a build. Instead of brew install, which mutates /usr/local and hopes for the best, Nix produces a content-addressed artifact under /nix/store/- and links to it from your PATH.
Two community projects do the work I actually wanted.
nix-darwin extends Nix’s model to macOS system-level state: login shells, launchd agents, defaults write settings, even Homebrew formulas.
Home Manager extends it to the dotfiles inside ~. Every knob and setting on the machine is exposed as a nix function, and the whole machine becomes one big derivation. You can build it, throw it away, and build it again, and the second build is byte-identical to the first. Really excellent tool if you’re looking for something to cover more than just zshrc.
For the actual Nix install on macOS I use Determinate Nix. The vanilla Nix installer works, but Determinate ships a friendlier daemon with some sensible default configurations and an official nix-darwin.
The full toolchain is Determinate Nix at the bottom, nix-darwin for system-level state, Home Manager for user-level state, and a Nix flake that pins all three to the same release.
Overall structure
manager/<br>├── flake.nix # entrypoint<br>├── flake.lock<br>├── install<br>├── bootstrap/install.sh<br>├── lib/vars.nix<br>├── hosts/ # host-specific configuration<br>│ └── AVA/default.nix<br>├── modules/<br>│ ├── darwin/ # nix-darwin to set macOS settings +...