Bouke van der Bijl

Yes, I use Nix

Nov 2020

I’ve been on the Nix train for about half a year now, and I have to say that it has completely changed how I look at my development setup. Before Nix, setting up my computer on the Mac was always a mess of Homebrew packages that I would install, being dependent on whatever’s available in the repository, and being forced to have everything installed globally. There’s also not a good way to keep track of what I have installed, there’s potential for conflicting packages, it’s just not great.

Nix changed all of that. I could now version check my environment, I could try out a package in a subshell before installing it globally, I can even easily pin a package to a specific version using Overlays. Installing things is very fast in general because Nix has great caching, which it can download and install from without any compilation or linking.

But what is it?

It took me a while to understand Nix and all the different projects that are affiliated with it, but the gist of it is this:

  1. Nix is a build tool that is aware of all the dependencies (files, other things that have already been built) for a dependency. When something is built, it takes all the names of the dependencies, and all the files that are needed and hashes those together. That hash is then the name of the thing you just built (hold that thought) and put into the ‘Nix store’—this resides at /nix on your system.
  2. All the Nix packages are set up to build in a way that they don’t reference anything outside of Nix. So when they build, then can only reference other Nix packages (for which that is also true etc. etc.) and files on the web by hash. So if you want to download the source code of something on the web, you need to put the sha hash of that thing in your Nix build instructions so it can ensure that the same thing is always used. The Nix build tool ensures that nothing outside the Nix store is used during build—by clearing out the environment variables for example.
  3. Once you build something in this way, you can reference it by linking to it from outside the store. For example, I can setup a Vim config and a bunch of plugins and then I can link ~/.vimrc to the Vim config after it was built inside the store. This link is to some hashed name.

This clever set up makes sure that if I can build a package on my machine, you will always be able to build it on yours as well, because Nix makes sure that all the packages that need to be built are unable to be influenced by all the other mess on our computers. This is super cool!

Fun tricks

I put my whole environment on GitHub at bouk/b which means I can check it out and install it very quickly. I can even install the same configuration on Mac and Linux, which is pretty mind-blowing! It just works.

You can run nix-shell https://bou.ke and get my whole environment in a subshell (note: it will take a while to build everything). I achieved that little trick by adding this to my Cloudflare Workers config:

const ua = event.request.headers.get('User-Agent');
if (url.pathname === '/' && ua.match(/\bNix\//) != null) {
  return new Response('', { status: 302, headers: { location: `https://github.com/bouk/b/archive/master.tar.gz` } });
}

Because it doesn’t matter what else is on your machine, Nix can safely build up my environment with the versions of software that I want and run it on your computer, without worrying about what’s already on there! So cool!

What I want development environments to be like

I got onto this whole Nix path because of my former colleague Burke’s writing and YouTube series over at Shopify. Nix can really shine in development environments, where you have to share a bunch of dependencies (a compiler, database, etc.) between a large number of developers. Being able to use Nix to specify all the programs that you’re depending on means you don’t need to tell people to install a bunch of crap onto their machine and fight with stuff just to get it all working, just let them install Nix and use its magic to make everything work.

What I want is for Nix Flakes to become a thing, so you also don’t need people to install stuff like direnv into their shell and have a daemon running like lorri just to have a decent workflow. No, what I want is to be able to write a bash script like this:

#! /usr/bin/env nix <something flakes something>
pg_ctl start -D ./tmp/postgres start

And have it run the exact same version of Postgres on everyone’s machines. Whenever this file is run, Nix should make sure that the locked dependencies are installed—if I understand correctly, then with Flakes this should be instant. Then it will run this script inside a shell that contains all the required things, so everyone can consistently share versions.

I’m not a Nix maximalist, I don’t think we need to have all of a projects dependencies in Nix. For example, I use Go a lot for projects and I’m not using some ‘Go module to Nix translation’ thing, because I can rely on Go’s package manager to do the right thing. I do want to use Nix to make sure that everyone is using the same Go version however. This strikes the right balance between developer experience and reliability, in my opinion.

Lately I’ve also been railing against Docker for local development. It’s nuts that on Macbooks we are running a virtual machine to run containers to run databases, when this should just be a process on your computer. The only reason people are doing this is because there is a need to consistently install dependencies, Docker is the only hammer people think they have in their toolbox. With Nix we could just run it on everyone’s computer directly, without nested virtualization and killing people’s battery. It’s aesthetically gross to run a whole VM to run a single process. But we can do better!