Note: I wrote the vast majority of this post on August 24, 2022, but I decided that it was a whiny rant that I didn’t want to publish. I came across the draft in January 2024, remembered that this entire series is a whiny rant, and decided to publish it anyway.

I’ve been a happy Nix user for about 18 months now, and– well, not happy happy, but satisfied… no… not really satisfied either; perhaps it’s more of a resigned disgruntlement; a feeling that despite its many flaws, it’s still better than anything else out there, and I’ve invested so much time into it already that it would be a shame to give up now, so… am I describing Stockholm syndrome?

I’ve been imprisoned in Nix’s castle for about 18 months now, and I recently came into a new laptop – one of those shiny blue numbers – so naturally one of the first things I did was install Nix on it.

It would be nice if this blog post were only like a paragraph long, wouldn’t it? Step one: run the installer; there is no step two.

But I am afraid to report: there is very much a step two.

a tale of two nixen

There are two flavors of Nix, and you must choose at installation time which variety you will run.

You can install “single-user” Nix, in which Nix is a binary that you run as an interactive command that does things and forks subprocesses and works like any other package manager you’ve ever used. Or you can install “multi-user” Nix, in which Nix is a daemon, running as root, and the nix command merely makes requests to that daemon, which in turn coordinates a set of special nixbld users that exist so that you don’t build all of your software as root.

If you’re using NixOS, you have to use multi-user Nix, which makes sense to me: Nix controls the entire operating system, and there needs to be some central puppeteer to coordinate changes to the kernel or whatever.

But if you don’t use NixOS, then you have a choice. You can choose to set up a persistent daemon, create a dozen nixbld users, and have every command you run proxy through this centralized service. Or you can choose… not to do that.

I don’t know why you’d choose a multi-user installation when the single-user option is available. There might be a very good reason to prefer multi-user Nix, even when you are installing Nix on a laptop with a single user, but I do not know what it is. Obviously there is no documentation to explain the distinction or persuade the user one way or the other, so I can only speculate.

And needless to say, when I first tasted the forbidden fruit of the Nix tree all those year ago, I chose a single-user install, and I have never regretted that.

But then something upsetting happened.

macOS has always been an unloved corner of Nix, and installing Nix on macOS has been rather fraught with peril ever since macOS Catalina came out.

The problem seems so trivial: Nix requires a /nix directory to exist, but macOS does not let you create new top-level root directories, so you have to actually create a separate volume and mount it as /nix. Which might not sound difficult, but getting that to work with FileVault and the various hardware encryption configurations of different generations of Apple hardware and… I dunno, probably some other complicating factors, I don’t know how computers work… there was a heroic effort to keep macOS Nix working at all, at a time when some Nix maintainers were arguing that it would be easier to deprecate support for macOS entirely.

But there was an unfortunate casualty of this struggle: the single-user installation option was removed.

If you want to install Nix on macOS today, you must use a multi-user installation. That means running nix-daemon, that means messing with launchctl, creating a bunch of build users…

Which brings things back to me.

I had a new laptop. I wanted to install Nix on it. And the only option facing me…

Was to get in there and hack up an unofficial, unsupported single-user install, because I just can’t abide the idea of a multi-user Nix install.

And it’s not only a philosophical objection! Although I admit that I have a visceral aversion to the idea of installing a daemon running as root for something that absolutely does not need to be a daemon or running as root, I can understand that many people do not care about that.

But there is a real, practical reason to avoid the multi-user configuration: if you are using a multi-user Nix install on macOS, then every time you upgrade macOS, Nix will break, and you will have to go in and do some surgery to fix it.

This was true when I wrote this, in 2022. I don’t know if this is still true, but an alternate Nix installer – which also only supports multi-user installs on macOS – puts “survives macOS upgrades” at the top of its advantages over the official installer. Which implies to me that this is still a problem.

This is because the multi-user installation edits some of the system-provided config files – like /etc/bashrc – to insert some Nix setup things. And every time (every time?) you upgrade macOS, it will overwrite these files and restore them to the defaults. Is it hard to fix? No. Is it annoying? Yes. Is it sufficiently annoying to try to figure out a single-user install? To each their own.

In any case: the only difficult thing about installing Nix on macOS is creating that darn /nix directory. And clearly the multi-user install step knows how to do that. So I should be able to steal that part of it, create the directory, and then manually perform a regular single-user install. Right?

Right.

right

Well it worked great, actually! It wasn’t hard at all. The steps are:

First, download the Nix install script.

The official Nix installation instructions are of the curl | bash variety. But if you actually look at the script that you’re curling, it just turns around and curls another script1 and runs that.

Actually a directory full of them. So the first thing to do is to get that directory of actual install instructions.

You can find them on GitHub, although I just modified the original script to download the installer, extract it, and then quit before doing anything else (er, and also to not delete the extracted artifacts on EXIT). The really interesting one is create-darwin-volume.sh, which, as you can probably guess, creates /nix.

But! You cannot just invoke this script by itself. This script is invoked by the larger installation script, which sets some environment variables that the create-darwin-volume expects to be able to read. Specifically it expects to be able to read…

You know what? I don’t think you care. Or rather, if you do care, I’m sure that you can figure it out from reading the script. It wasn’t very hard, and by the time you’re reading this the precise interplay between the two scripts probably changed anyway, and you would have to dive into the code to figure out what the modern version requires anyway.

Once you’ve created your /nix, modify the install script to remove the lines that force a multi-user install on macOS:

case "$(uname -s)" in
    "Darwin")
        INSTALL_MODE=daemon;;
    *)
        INSTALL_MODE=no-daemon;;
esac

Then run the install script, and you’re done.

or just, like, just make a multi-user install

It’s the future now – I wrote this section in 2024, which is part of why this segue is so abrupt.

So, I didn’t do that. I am still running the single-user nix install that I hacked together a year and a half ago. I was kind of worried initially that I was overlooking something that made this more complicated and it would break the moment I upgraded Nix, but no: it has held up perfectly. I don’t know why the official install script forbids single-user installs on macOS, because they seem to work better than multi-user installs – I have never once had to think about Nix during a macOS upgrade.

But I feel that I should correct something that I wrote previously: there are actual reasons why you might prefer a multi-user install, even on macOS.

It’s actually kind of nice to run Nix builds as special nixbld users. Those users only have permission to read and write to the Nix store; they can’t accidentally stick config files in your home directory during install time, for example, or reference a source file that you didn’t actually copy to the store. Whereas, with a single-user install, it’s possible to write Nix expressions that – accidentally or not – depend on files anywhere on your computer.

So the multi-user install has two advantages:

  1. You get some level of sandboxing/protection from malicious packages
  2. You get an error when you write a non-hermetic nix expression, instead of silently succeeding

Those advantage don’t seem worth any added complexity to me, let alone having to deal with upgrade problems, but not everyone uses Nix exactly the way that I do.

(1) is interesting from a security perspective in theory, but since I usually download pre-built derivations from the binary cache and then run it as myself, I am already vulnerable to malicious nixpkgs. Not as vulnerable, perhaps, if the binary cache did some kind of fancy virus scanning of uploaded executables… but we’re in the same ballpark.

I don’t really care about (2) at all, although I could see this being useful to people who regularly share Nix expressions with other human people. The cost of making a mistake here just seems so low to me, and the fix is so simple, but I guess if you make this mistake very often it would be nice to hear about it right away.

There might be even more advantages than these! But we have once again reached the limits of my knowledge.

why do you care so much

It’s 2022 again.

I can’t really explain why I’m so averse to a multi-user Nix installation. But I am.

And I know that if I had met Nix just a little bit later in life – after 2.4 was released, when the single-user install option was deprecated – I never would have come back to Nix in the first place.

Which is a shame, because I really do like Nix, and as much as I like to razz it, I still prefer it – philosophically and practically – to Homebrew.

So I am writing this post to say: you can still install Nix on macOS like any other package manager. You don’t need to take one look at it and go “whoa what the heck I’m not setting up some root-user daemon just to download packages from the internet” and close the tab. You can still use it like you would use any other package manager. You just… have to get your hands dirty first.

Which might actually be the perfect introduction to Nix, come to think of it.


  1. Amusingly, even though the first step included no checksum (though you can find one if you care), this script does checksum the downloaded file. Maybe because it’s served from a different subdomain? I don’t know. ↩︎