Migrating from Homebrew to Nix for OSX

#1

I came across a nearly-month-old Reddit thread about this topic here, and wrote up a long stream-of-consciousness reply that I wanted to reproduce here - both to help drive traffic to that Reddit thread and to garner feedback about what I wrote and my own experiences.


I strongly recommend using home-manager rather than imperative nix-env -i style commands, and would also more lightly recommend nix-darwin. I have significantly less time with nix-darwin, which is part of why my endorsement is much gentler. I’ve used Nix on OSX since ~August 2017 and nix-darwin only since ~November 2018, and I know I’m not using it to the fullest. I find the latter’s learning curve to be something of an impediment for people who have migrated from Homebrew without a lot of prior Nix experience. To my knowledge, it’s the only way you can reasonably approach the utility of brew-cask while using primarily Nix instead.

Over time, I’ve managed to shrink from a list of 100+ homebrew packages and nothing installed via Nix to almost-everything with Nix and a few holdouts in Homebrew. My brew leaves shows only 13 packages today, mostly things that are entirely unpackaged in Nix, or awkward to use vs Homebrew, such as my clinging to brew services for purposes of Postgres 10.x and not learning enough about nix-darwin to write myself an equivalent launchd definition.

I definitely also recommend starting your journey by taking a capture of everything you have via Homebrew today, by running brew bundle dump --no-restart and saving that in ~/.Brewfile. This will allow you to have a point in time capture of that inventory, and also enable the following commands:

  • brew bundle install --global --verbose --no-upgrade
  • brew bundle check --global --verbose
  • brew bundle cleanup --global --force

Judicious use of the first and last can get you moderately close to “declaratively” managing your Homebrew inventory and see how it changes over time. The file is arbitrary Ruby in content and you can easily revise the file to add conditionals for things like your hostname, OS version, etc. if you want to share it between machines.

A few other random pointers:

  • I alluded to it earlier, but I chickened out of trying to manage my GUI software with Nix and continue to use brew cask for this, YMMV
  • It is entirely possible to succeed with the multi-user daemon-based Nix install, including on OSX Mojave. There’s a GitHub issue I can link that has to do with process forking and curl if other people have problems with this combo, but mine is working happily after that particular triage.
  • Get used to scoping out brew leaves to see what you currently use from Homebrew rather than brew list which will lump in dependencies.
  • Start with brew unlink instead of brew uninstall, don’t just go cold-turkey, especially if you rely on this stuff professionally
  • Use some variation of nix-env -q (I like -qaP) together with brew info ... to see if your needed packages are drastically older on Nix, which has happened to me infrequently but non-zero times.
  • It’s not strictly necessary, but getting comfortable with writing your own derivations might ease the pain of migrating at the cost of some maintenance burden.
  • Try to take some time to learn the semantics of nix-store --gc and nix-collect-garbage (and home-manager expire-generations if you pick up HM), and keep an eye on disk usage of /nix at first using something like DaisyDisk or the CLI utility ncdu. I keep a rolling ~60-day window and don’t automate the cleanup, and I have 10s of GB in my store at the moment.
  • Turn on auto-optimise-store in /etc/nix/nix.conf unless you (or another poster) know a good reason not to - it hardlinks duplicate content for you and can save quite a bit of disk space.

If you want to see what kind of insanity is possible for someone who’s really comfortable with this intersection, do some spelunking in John Wiegley’s dotfiles. Even with the experience I’ve mentioned up top, and being comfortable with Emacs/Make/etc., there were still some real brain-benders in here for me.

2 Likes
#2

If you’re the only user of the machine, is there any particular benefit to this? I’ve always assumed the answer is “no” so I’m running a single-user install of Nix (but have not tried home-manager or nix-darwin yet, I’m rather nervous about retrofitting my existing setup with either of those).

Edit: John Wiegley’s nix-config includes a file install.org that lists installation instructions, and it explicitly says to use the multi-user install of Nix, but it doesn’t explain why. I’m not sure if that’s supposed to be general-purpose advice, or if it’s just done this way because the Makefile assumes multi-user (e.g. using sudo for certain operations).

#3

You know, in all honesty, I never reflected much beyond taking the recommendation in the Nix installation docs at face value:

As of Nix 2.1.0, the Nix installer will always default to creating a single-user installation, however opting in to the multi-user installation is highly recommended.

Not sure if that is just as true for a single-human OSX machine as it might be for a Linux workstation, with or without multiple humans’ involvement.

#4

So the difference between the two is:

  • Single user runs builds as your local user. This is most similar to how Brew works and is probably safe if you are just using Nix to install packages (as opposed to a development environment). It’s much simpler to understand, but also leads to issues where your user has special permissions on your system.
  • Multi user has a daemon that is called every time you build something. This gives us better isolation and also enabled things like sandboxing to work. I would recommend this for people who are developing on Nixpkgs because it gives you the best chance of things working both locally and on OfBorg/Hydra/any other CI.
1 Like
#5

I guess everything’s always relative. I spent some time in December/January working on scripts and nix configs to stand up a new laptop using (single-user, for now…) Nix and nix-darwin. No home-manager. (@lilyball I had similar concerns; this was an ideal way to move over to using nix without risking my productive laptop. If you don’t think you’ll be in the market for an upgrade soon, I’d suggest seeing if you can find someone with an older SSD macbook who is looking to upgrade and try offering them ~ what Apple would give them for the trade-in, or perhaps asking if you can rent it for a month or two.)

I was hoping to cut homebrew out of my system entirely, but it wasn’t practical for GUI apps. I fought this for a while, but came to the conclusion that the ideological purity tax is still too high for now. Many of the GUI apps in question want to auto-update themselves anyways. I have 15 casks in my brewfile; brew manages nothing else.

I still need to write new shell.nix files for a few old projects that I haven’t touched since I moved over, but nix is so much better at managing my dev dependencies that I literally would not let brew do it unless the package wasn’t in nix and I ran into significant trouble packaging it.

The main thing I needed to do here was go through all of the brew-installed packages I had to separate anything that was a user tool/util from dev dependencies for various projects. I think I spent a few hours on this, but in retrospect I should have just backed up a copy of the list, pulled everything I recognized out, and moved on secure in the knowledge that I could consult the list later.

1 Like
#6

I cut Homebrew out of my life quite some time ago (let’s just say the lead maintainer is not a good community steward), but the only GUI app I ever used it for anyway was MacVim, and I now use MacPorts for that (and for a handful of other tools that didn’t have up-to-date Nix packages). In general I’ve found that GUI apps generally self-update, so I don’t need a package manager for them.

1 Like
#7

I think this is a perfect example of a missing tutorial that we should have in the currently non-existing tutorial section :slight_smile:

2 Likes