Moving to Nix
Some time after fixing my Neovim config, I decided to follow up on early search result hints that maybe I should be using Nix instead of Homebrew to manage my configuration. I think the catalyst was two HackerNews posts that crossed my feed recently:
- My Linux Odyssey: How I Ended Up on NixOS, which links to Dimitri Nesterenko's blog; and
- Asahi Linux 7.1 Progress Report, which links to the Asahi Linux blog.
Anyway, I went to Back Market and ordered myself a refurbished MacBook Pro with an M2 chip in it and set about installing NixOS on it. The installation went a lot more smoothly than I was expecting but I immediately faced analysis paralysis when it came to actually configuring the thing. I decided to simplify a bit and start by using nix-darwin to configure a dev environment on the macOS partition that the NixOS install leaves behind. As it is, that's a rather daunting set of decisions to make.
I ended up settling on the following configuration:
- I'm using flakes, rather than channels. This choice is described as "experimental" basically everywhere I look, but it's also apparently the better-tested choice on Darwin, specifically.
- I'm following the 26.05 release rather than "unstable". I don't really know, yet, what the consequences are of making this choice but so much of what I'm doing is new right now that I figure it's better to rely on "blessed" builds rather than riding the cutting edge.
- I chose to separate my "system" configuration from my "user" configuration.
- The system config lives in
/etc/nix-darwinand requiressudoaccess to make any meaningful use of. - The user config lives in
~/.config/home-managerand can be updated and used as a regular user with Home Manager.
- The system config lives in
- For my Neovim config, I'm using Nixvim. This is yet another layer of new abstractions, but it feels like the right long-term direction, and my Neovim configuration is pretty minimal right now so it's still pretty understandable.
- I'm version-controlling my system configuration and my user configuration with Git in separate repos on GitHub.
The experience with nix-darwin has been mixed-but-positive. There's a lot of documentation out there but it's of highly variable quality with a lot of cargo-culted copypasta. I found it a bit overwhelming to research everything myself and stooped to asking Claude for help. I was actually pleasantly surprised how useful the free Claude web UI was (using Sonnet 5 in "medium" thinking mode). Here are the conversations I've had with Claude so far, in roughly chronological order:
- Understanding the integration between nix-darwin and Home Manager.
- This was the foundational conversation that helped me figure out that I want a two-repo split between system and user config. It's also where I got the overall architecture of my two configuration repos, with a modular layout, and a common/darwin/linux split. Talking to Claude also helped me choose Nixvim, the MIT license for my public repos, and a bunch of other things.
- The first pleasant surprise was that Claude was able to follow the link I provided to my first post on this blog documenting my Homebrew-based configuration and use what I'd written there to provide a good approximation of the same config expressed as Nix expressions.
- The second pleasant surprise was that, when I asked for a summary of the conversation, the result was a downloadable, Markdown-formatted "document" rather than what I expected: a long chat answer. There were some things in the summary that needed tweaking, but it was a great baseline to work from.
- Refining the nix-darwin configuration.
- This was effectively a side conversation I had in a separate tab part way through the previous conversation. Upon opening a new chat, there was a prompt announcing Claude's new "memory" feature so I opened by asking if Claude remembered my previous question; the answer was no but uploading the previously generated summary turned out to be a good substitute.
- In this conversation, I narrowed down some of the finer details in the proposed configuration structure, and learned more about how to configure things "the Nix way" (assuming Claude can actually teach me "the Nix way"). For example, I have a switch in my user configuration that allows me to conditionally install Bash as a user package only if the system doesn't provide bash for me. I also learned that Nix can delegate management of some packages to Homebrew, so I have scaffolding in place to configure the packages so managed, but the same lesson convinced me that I'd prefer to have Nix manage as much as possible and only fall back to Homebrew when necessary, so the scaffolding is not currently active.
- Configuring iTerm2 to my tastes.
- This time, Claude responded that it did "remember" the ongoing conversation regarding my burgeoning Nix configuration; the memory feature seems to be quite useful so I'm now wondering why I was asked whether I want to enable it—perhaps there's some privacy or security concern I should be worried about?
- I used this conversation to create an iTerm2 profile that matches the one I previously built manually several times. It uses the FiraCode Nerd Font that I originally found here, with ligatures enabled, and runs a non-default command so that my shells run as separate
tmuxwindows, each in their own native iTerm2 window. Along the way, Claude offered to draft a "PR-ready commit" for the changes it had proposed; when I accepted that offer, it generated what looked like an LKML-style email with patch attached and instructions for apply the patch with Git. It worked like a charm. - This conversation also taught me more about how
ssh-agentworks on macOS, and how macOS's session management works. One result of these lessons is that my iTerm2 profile uses a Nix-managedtmuxbut the Apple-providedssh-agentso that SSH keys are shared between all processes descendant fromtmuxand all the GUI applications I happen to launch in the same session.
- Debugging ligatures inside
tmux.- In my manually-managed profile, I'd noticed sometime recently that FiraCode's ligatures had stopped working in Neovim but I never stopped to figure out why. I noticed the same problem in my new Nix-managed profile and I'd been having a lot of success with Claude so I decided to debug it. The solution came back in the first response: reconfigure iTerm2 to not use a general "tmux" profile for the windows it creates to render
tmuxwindows and, instead, use the profile of the window that launchedtmux. It took a little more digging to get a declarative configuration in place that fixes the issue, but that went quite well. - Now that ligatures were working as I wanted, and Claude was proving to be a useful tool for getting my configuration just so, I also decided to see if it could help me get rid of the annoying one-time prompts that happen on a fresh install like the one iTerm2 shows when you press Cmd + N in a
tmuxwindow to ask whether you'd prefer a newtmuxwindow or a new window with the default configuration. I want the former, and I'd rather not be asked about it; Claude helped me update my Nix configuration to express both desires permanently.
- In my manually-managed profile, I'd noticed sometime recently that FiraCode's ligatures had stopped working in Neovim but I never stopped to figure out why. I noticed the same problem in my new Nix-managed profile and I'd been having a lot of success with Claude so I decided to debug it. The solution came back in the first response: reconfigure iTerm2 to not use a general "tmux" profile for the windows it creates to render
- Configuring my Bash profile.
- I don't like the default Bash prompt provided by a bare Nix install. I think
$PS1is left unset, and Bash must default to showing its own name followed by a$, leaving me with the uninformativebash-5.3$as my prompt. I've gotten used to the default provided by Apple in a fresh install, which, it turns out, is\h:\W \u\$, which translates to something likemacbookpro:home-manager ianpetersen$(I think that'shostname:$PWD username$, except that it's only the leaf-most directory, not the full working directory). I discovered Apple's default by poking about in/etc/profileand/etc/bashrc, and so I asked Claude for help migrating those defaults to my Nix config. - Claude warned me off replicating Apple's use of
/usr/libexec/path_helperand taught me the difference between~/.bashrcand~/.bash_profileso my Bash configuration is now set up to better match the interactive/non-interactive split. I also learned more about the difference betweenexporting shell variables and not doing so, and worked out that I don't need to setBASH_SILENCE_DEPRECATION_WARNINGnow that I'm not using the Apple-provided Bash 3.something. All in all, quite a productive conversation with the LLM.
- I don't like the default Bash prompt provided by a bare Nix install. I think
- Configuring Rectangle just so.
- I've had Rectangle installed through my Nix configuration for quite a while by this point, but it wasn't launching on login so my reflexive Ctrl + Opt + Left/Right wasn't working. I've been delaying actually launching it manually in hopes that Claude could help me figure out how to update my Nix configuration to minimize manual friction here and I got a better outcome than I could have expected. With Claude's help, I've configured Rectangle to:
- launch on login,
- not prompt me about launching on login,
- not prompt me about which shortcuts I want and just pick "Recommended", and
- not warn me about shortcuts that conflict with macOS and just disable the macOS ones.
- Along the way I learned that some of the prompts that were annoying me are security features of macOS that can't be "configured away" and that Claude can (sometimes autonomously) go get the source code for random programs I'm trying to configure and work out how those programs interact with their configuration to tell me how to get the setup I want.
- I've had Rectangle installed through my Nix configuration for quite a while by this point, but it wasn't launching on login so my reflexive Ctrl + Opt + Left/Right wasn't working. I've been delaying actually launching it manually in hopes that Claude could help me figure out how to update my Nix configuration to minimize manual friction here and I got a better outcome than I could have expected. With Claude's help, I've configured Rectangle to:
There are probably a whole bunch of things I'll be tweaking in this configuration over the next months and years, and I'm not even sure what will come next. For sure, though, I'll need to address the warnings I get when I run home-manager switch; there's some problem with the way I've configured Nixvim and I haven't been able to figure out how to fix it so I'll probably ask Claude again. Meanwhile, I'm working on configuring a nix develop shell for Sendosio, the previous nerd-snipe on my stack of nerd-snipes. I've got Clang 22 and cmake --workflow llvm-debug working in my Nix-managed macOS environment, but my attempts to use Google's Gemini to help me get GCC 16 working (which would give me access to C++26 reflection to explore a new nerd-snipe) failed miserably. I'm curious whether GCC 16 would be easier to get working on the NixOS install on this machine because work-arounds like Skarn's PR handling __need_rsize_t in Iain Sandoe's Darwin-focused branch of GCC would be unnecessary, but I'm going to resist temptation and pop my stack rather than pushing again.