NixOS Pins and Needles

A desktop workspace on NixOS.
A desktop workspace on NixOS.

NixOS is a Linux distribution built around the nix package manager. Since writing my first article on NixOS, it has quickly become my OS (Operating System) of choice for desktops, servers, and Replaced my pfSense and OPNsense routers with NixOS. The transition process to become almost as Good old cowboy style system administration. as my imperative environments has been marred by a lot of pins and needles on the usability front, but it has been worth it.

For context, you can use a single configuration.nix to build machines which is super convenient, but my inventory is diverse and has at least five machines. Configuring these machines to play nice together ultimately means having to control complexity — and that’s not always fun.

This is a fairly opinionated and under the hood take on using NixOS, you shouldn’t take any of this as advice — I’m not a functional or LISP programming expert. Lasciate ogni speranza, voi ch’entrate.

Testing Single Files Quickly

My inventory includes a lot of low end hardware so rebuilding even a small subset of machines is What software today isn't slow? Fast feedback loops are hard to create nowadays. in NixOS. This odd hacky script of mine dry builds single files in isolation and foregoes the need of rebuilding entire configuration sets (with some trade–offs), saving precious time. Before, I’d usually have to wait for a complete nixos-rebuild or nixops evaluation, only to discover missing imports or other syntax errors. Single file evaluation allows working on small sections of a larger set of configurations quickly without rebuilding or activating machines.

Using my hacky script nixos-test to quickly evaluate small files and even a full configuration.

GNU Guix (as in geeks) is currently superior to NixOS in this regard with commands like guix system that provide this type of fast inspection. If Guix is interesting to you, Emacs and Guile Scheme. out this cool video introduction.

Searching For Packages and Files

In most Linux distributions you can search to see which package owns a file. For example, on Arch Linux you would run pacman -Fy <file> to search the Random Aside: It appears that Valve’s Steam Deck is based on Arch Linux. That's interesting. database for a package that contains the <file> you’re seeking. This is great when compiling or writing a program. On NixOS, my preference is to use nix-locate to find packages and files. NixOS package search and naming conventions can be unintuitive. To ease my pain, the ideas of this shell script are adapted for my specific purposes. My hacky version sets up an alternate home environment, drops the desired program into a temporary nix-shell with fish and prints some useful paths afterwards.

Poking around VLC's package files.

Running Configurations

NixOS comes with powerful virtual machine integration testing modules. I’m a bit more lazy and like to jump directly into a virtual machine from my shell with QEMU (sometimes called Quick Emulator) to try out a configuration. The nixos-generate command generates different system output formats for a specified configuration. The ISO Optical disc image. is my favorite. Often times I’ll spin up a system in the cloud as a read only ISO.

$ nixos-generate --format iso --configuration server.nix --option builders ''
$ qemu-system-x86_64 -nographic -enable-kvm -m 1024 -cdrom nixos.iso
Trying out a configuration with a virtual machine.

DRY is Evil Incarnate

The functional ecosystem has taught me that I'm not that smart anyway. programming isn’t worth the pain. Don’t repeat yourself (DRY) is the devil incarnate in my book, especially in a functional programming context. After throwing away my first directory structure, I’ve since implemented what may be a kind of naive stratified design approach to prevent myself from wasting Sometimes coming back to my module configurations leave me baffled. pulling useful logic out of deep abstractions or forcing my poorly crafted NixOS modules to work for a purpose that it was not designed for.

Nixpkgs, the NixOS implementation, lets you spin up system abstractions in your sleep and while this is good for writing packages The domain specific tooling of NixOS and Guix is amazing for writing massive amounts of system packages quickly. if your hastily made abstractions are poor — you’ll be banished to decomposing the world. My self made rule now is that anything that does not require imports and does something useful is a basic knowledge piece. Every singular piece of knowledge is saved, and whether used or not can be combined to create higher functionality independent of itself. These knowledge pieces should be easy to change, check, and reuse. For that simple reason, duplication is good, really really good, and as a bonus — sharing working snippets with others is easier.

WET: Write everything twice.

Modules Should Do One Thing

Or rather — it seems useful to distinguish between lower level modules (modules containing systemd modules only) and higher level modules (modules containing multiple modules). The high level interfacing inside a module is often too hard to pick apart. Situations can arise where you try to do something with a module that is out of its scope. The reality is that a lot of NixOS modules have two or more smaller modules struggling to get out. Sometimes you’ll have to reduce upstream modules down to its basics to get what you want done — this can be painful.

My use cases typically require running multiple copies of the same module. The easiest way to achieve this is to keep custom made modules as small as possible (just a systemd service and basic configuration). It’s impossible to satisfy all use cases so everything else ends up as a test case of combinations that can be used as higher level modules. “Lower” level modules are easier to understand and manipulate too, since they are just an indirect version of plain systemd files.

The lib.attrsets.mapAttrs' function is great for duplicating systemd services.

Separate System and User Environments

As a user, if I can avoid a nixos-rebuild, nixops deploy, sudo, or doas — sign me up. My user packages are split from the system side with config.nix abuse and nix-env. The nix community’s home manager is nice, but avoided because the less dependencies the better when connecting my home folder to another Linux distribution. My programming style incorporates the Unix shell mostly as an integrated development environment (IDE), so my dotfiles and default presets for linting and such are all that’s necessary. The new nix flake feature puts in the guard rails for proper reproducibility, but I don’t think I’ll be using that for some time.

Imperative to Declarative User Experience

Once in a while I’ll take a look at the discussions on social media around the NixOS, Guix, and functional operating system community. Recent discussions seem to center around the lack of popularity of NixOS and GNU Guix and their complexity.

The unpopularity of NixOS and GNU Guix sounds about right. Functional Linux distributions will not become generally popular anytime Sometimes getting popular too fast has more drawbacks than benefits anyway. due to misaligned developer and user experience expectations. Popularity may be limited to self–hosting, developer operations, and system administration circles.

I’m fairly certain that nixos-generate-config is the command with the best user experience (UX) on the whole system — everything else is pins and needles. NixOS needs more imperative to declarative End users don't want long form "documentation" — what they really want is either copy and paste style short documentation, or imperative commands that generate plug and play configuration. In the end, as the joke goes — users are programmers who believe anything you say. The NixOS install procedure achieves this workflow with two separate commands. The first command generates a declarative configuration and the second implements it. If distribution installation speed runs were a thing, then NixOS would have a leg up especially if a configuration.nix is baked into a custom installer with automatic formatting options like fileSystems.<name>.autoFormat.

 $ nixos-generate-config --root /mnt
 $ nixos-install

In my experience, a casual NixOS user doesn’t even want to open the configuration.nix to manually write stuff and connect modules together unless absolutely necessary. In their world — configuration blocks should simply be “generated” so that they can just copy and paste things.

“Why can’t it just generate the configs like it did to install my system?” A casual user

Docker, Kubernetes, Tailwind, and React all have the same Synergy — but I like to call it copy and paste–ability. Applications with high copy and paste–ability are always popular. incentives that ensure popularity. It’s not necessarily the underlying technology that makes a software ecosystem popular or “good”, but the ease of the higher level tooling to create, share, and copy from a near endless library of pre–defined documents, templates and components. In short, it’s very little effort for great reward — that’s the nature of the beast. Most users abhor starting from scratch and because of that it’s sort of easy to anticipate what will become popular — NixOS and GNU Guix are not there yet. Searching the NixOS options index to build up configuration is almost like starting from scratch in a docker compose.

Conclusion

Future iterations of NixOS, Guix and its derivatives can only get more robust. Unchanging state. may be rejected in most real world applications, but Describing what a result should be. will definitely become the name of the game. Describing how you want to get to a result. styles will undoubtedly always be around.

If easily composed and shared declarative tooling (infrastructure as code) becomes the dominant paradigm in user space, then issues in the Linux desktop space can be resolved elegantly by lassoing the majority of desktop configuration types and quirks. The NixOS hardware quirks repository is brilliant.

Interestingly enough, the nixpkgs repository is a very Hosted on GitHub. way of getting a general overview of the current state of the upstream software development world. Every dirty hack, every non–standard project setup, every odd network call during installation — the sandbox sees it all.

Finally, The nix language is just great in my opinion, without functions, it reads like JSON (JavaScript Object Notation) with comments minus the dangling comma shenanigans. It can be used as metadata or as a language. If I could ever manage to get my NixOS configuration under control (specifically secrets and metadata), I’d open source it, but for the time being, I just don’t have the time.