At the beginning of this year, I had read a lot about Nix and felt pretty confident that this wasn’t something for me. That was, until I did a voicecall with my good friend Victor. He was showing me these really neat tricks on how I could use Nix and Nix Flakes to handle configuration files, build software reproducibly and define my own development shell for each project I’m doing - I was blown away.
After that call, I immediately started with the (admittedly stupid) thing that every developer does when learning about a new technology. I started moving all my shit over to it: System configuration, package management, package configuration, etc. - I wanted it all reproducible. In this blog post I will document my experience so far. This is very far from a guide to using Nix, but you might find some useful hints from my mistakes.
Okay, the first step was to aquire Nix. I use a Macbook Air M1, and I quickly discovered that Apple have made this a hastle. After Catalina, you are not able to write to the root directory of Macs, which meant getting the /nix
directory up and running required hacky workarounds. Luckily, these are now intergrated into the installer, which made the install very simple.
$ sh <(curl -L https://nixos.org/nix/install)
did the trick for me. A reboot was needed, but then everything was up and running smoothly. Nice!
The next step was to get my system built through Nix. This is a pretty wacky concept, but the advantages are incredible. Think of all the times you’ve bought a new computer or needed to do a factory reset. All those preferences you’ve tinkered with to perfection over the years: They do not need to be set up again! A git clone
, one simple command, most likely a few minutes of building and you have all your apps and software up and running.
I started with this guide and tinkered a bit according to my system:
{
description = "kmaasrud's system";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
darwin.url = "github:lnl7/nix-darwin/master";
darwin.url.nixpkgs.follows = "nixpkgs";
};
outputs = { self, darwin, nixpkgs }: {
darwinConfigurations = rec {
mba = darwin.lib.darwinSystem {
system = "aarch64-darwin";
modules = [ ./configuration.nix ];
};
}
};
}
I put some simple system configuration in configuration.nix
, built the flake with
$ nix build .#darwinConfigurations.mba.system
and was joyful to see everything working. This gave me a symlinked directory named result
, containing a binary sw/bin/darwin-rebuild
which I could use to bootstrap the system from the same flake. To my dismay, I was met with an error that said I was missing the /run
directory. The error message said I needed to include the line run\tprivate/var/run
in /etc/synthetic.conf
, which would create the firmlink I needed. I did this without luck and ended up giving up on the whole debacle after a long night of googling. That following morning - when I could think sensibly again - I had an idea only a genius like me could find: I did a reboot…
It worked.
Okay, 1-0 to Nix, but I had no reason to give up now. darwin-rebuild switch --flake .
was working, and I was off to the races! The following days, weeks, months, I have been tinkering and my Nix system configuration to the core, and I am nowhere near considering myself done. If you are curious, you can explore my config yourself on GitHub.
The whole system configuration thing is very cool, but I’ve found that the real magic of Nix comes when you start using it in your projects.
Nix serves as a very flexible build tool for your software. The builds are done inside a siloed environment, which means you need to declare all the dependencies of your build. This is immensely useful! Software is built the same on all computers, which especially makes CI a lot easier.
In my project MDoc, which depends on Tectonic (and subsequently a bunch of C/C++ libraries), I am able to do the build in a shell that has all the required dependencies without actually having to manually install said dependencies. They are listed in the following way:
with pkgs; [
buildInputs =
fontconfig
graphite2
harfbuzz
icu
libpng
perl
openssl
zlib] ++ lib.optionals stdenv.isDarwin [
ApplicationServices
Cocoa];
Nix uses this list and makes sure these are linkable when building - so smooth! As you can see, software is built the same on all computers
wasn’t an entirely true statement after all, since I need some specific dependencies for Darwin systems. However, when they can be conditionally defined this way, who am I to judge.
Allright, that is a misleading header; My Nix journey has only just begun. What I can say, is that Nix has changed the way I look at software. It would be very hard to go back to the old ways of a mutable system and non-reproducible development environments. I am convinced that Nix is the future - in some way or the other. It just needs a facelift, because there is no way I am accepting With Signal on Desktop and iPad, you can link your primary Android or iOS account with another device, letting you check and respond to messages in both places or conduct video meetings and calls from the comfort of a bigger screen. Signal’… via Signal Blog January 27, 2025 Over the last six months, the Rust project has been working towards a slate of 26 project goals, with 3 of them designated as Flagship Goals. This post provides a final update on our progress towards these goals (or, in some cases, lack thereof). We are curre… via Rust Blog January 23, 2025 It’s out! On December 29th, I finalized DASCH Data Release 7. Depending on how you want to think about it, this data release represents the culmination of two years of my effort on the project, or of 19+ years of work on the DASCH project, or maybe even of m… via PKGW January 22, 2025nix-*
type commands.Here are some posts from sites I follow
A Synchronized Start for Linked Devices
December Project Goals Update
DASCH Data Release 7