NixOS is a novel GNU/Linux distribution started in 2003; it’s built upon the Nix package manger which provides a functional, declarative approach to package management. NixOS takes the direction of Nix, which can run on any Unix-like distro (including OS X), and continues further to allow control over the entire OS, from the file system to various services like SSH and HTTP, using the same declarative syntax. This means an entire NixOS setup, including all services, packages installed, and even configurations, can be represented in Nix configuration files and, potentially, stored some place like Github.
NOTE: Two years later, I’ve followed up with this post.
Similar to how software like PlayOnLinux works, where each program installed has its own WINE context and is stored completely separate from all other programs (for ideal stability), Nix takes a completely revised approach to storing both binaries and libraries in the Linux file system. In functional programming terms, the typical file system of a Linux machine is global, mutable state. Everything lives in one place, either
/usr/local or similar. This means it’s very difficult to, for example, have several versions of the same library installed at once. It’s also difficult to atomically upgrade and revert entire states of the file system.
As a novel way of avoiding dependency hell, Nix stores all installed packages, including system libraries, in
/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3). Each package’s dependencies are defined explicitly and Nix uses symbolic links to ensure that, when using a given package, all of its dependencies are made available. More info is available in the Nix manual.
At this point, it may not be clear why NixOS is worth exploring. For anyone who has used Chef, or similar software, to describe the construction of machines, especially for replication in clusters (using, say, Vagrant), NixOS provides even more control over the exact state of the machine. The biggest win is, since the entire system is controlled by Nix files and every bit of configuration is described explicitly, you can turn any NixOS machine into something complete different instantly, without rebooting. Let’s get to some examples.
My Nix files describe the setup I was building, which is my desktop workstation where I do primarily C++ and Clojure development. I use vim, watch movies with mplayer, listen to music with cmus, etc; all of this is described within these configuration files. The nice thing about declarative configuration is that you describe how the system should look when it’s ready not how it should get there.
Describing a NixOS system
If you’re doing it right, everything you need is in the form of Nix files. We can start with describing GRUB, instead of using the typical grub commands and mutating
/boot/grub/. After that, we might describe our network configuration, which includes a firewall. We can describe each user and the groups they’re in, the permissions they have, their shell, home directory, etc.
Once the base system is setup, we can describe how we want X to behave, which even allows installing proprietary nVidia drivers and setting up kernel modules, if needed. From there, specifying any number of packages we want and any configurations we want for them is entirely up to what’s described in the Nix files.
Once you’re in a NixOS machine, you can change your configs and then just run
nixos-rebuild switch and Nix will realize everything you described, even if it means building a new kernel, bringing in new software, dependencies, upgrading something, or performing big changes like switching from KDE to GNOME.
An example of just how simple something like a kernel upgrade can be: this commit upgrades my machine to Linux 4.2.3. No longer should you have to waste time reconfiguring machines to be similar to other machines you’ve setup.
What’s with the hashes?
In the previous example of how packages are stored, I mentioned the pattern
/nix/store/<hash>-package-version. The hash for each of these packages is derived from all of the configurations you’ve specified for that package, plus all the sources and dependencies for that package. There’s a NixOS build server, Hydra, which routinely builds and tests packages from nixpkgs. If it turns out that your required hash matches one built by Hydra, it means that the packages are equivalent and you can download the pre-built binaries instead of building locally. For highly configured packages, Nix will build all required sources for you, locally.
This makes having multiple versions of a package trivial, since each one is stored in a different directory. Only when a package depends on a certain version of another will they be linked together.
Though it may sound like I’m just trying to sell NixOS here, I’m not actually using it anymore. Why not? First, I want to say more about what I love with regard to Nix and NixOS.
Easy install of any packaged software (steam, skype, etc)
- By just describing that the package should be installed, in your Nix files, and telling NixOS to realize what you described, you can completely rework your system
- Seemingly large changes, like a kernel update, are often a one-liner
Your entire system is stored in configuration files which can easily be put onto Github
- Once you describe a machine, you can replicate it anywhere. If you lose that machine, the configs will bring you back to where you need to be.
Upgrades are atomic and you can rollback any changes
- By default, when you tell NixOS to realize your configs, it takes a snapshot of your current configs and updates GRUB so you can even choose previous setups at boot.
Installation is a breeze
- I installed NixOS from my existing Slackware install!
- The IRC channel on Freenode, #nixos, is quite active and helpful
With all of that said, I found that NixOS isn’t suitable for my development workstation. I spend a great deal of time compiling software which has various dependencies. In NixOS, in order for those dependencies to be available, one needs to use
nix-shell with some Nix file which describes the dependencies of that shell (boost, SDL, etc). Along with that, compiling arbitrary software which functions as a plugin of Nix-packaged software can be a real pain in the ass (namely Vim plugins with compiled components). This really left me in a bind, as I wasn’t able to freely compile the components I needed in order to do any actual development.
nix-shell is needed to build anything sane
- One needs to describe every single package which is required, otherwise NixOS won’t have them linked into the current environment. Unfortunately, even if they are linked in, I still ran into a plethora of issues with software like CMake not being able to find the right packages. This was my biggest pain.
The documentation is either awful or nonexistent
- NixOS isn’t that new, but it really only has a manual. Unfortunately, the manual doesn’t even cover all of the aspects. Some are in the man pages, others exist in various blogs, and others are only in the minds of those dwelling on IRC. Getting going with NixOS is a difficult experience; trying to use NixOS for anything other than exactly what’s intended is also painful.
nixpkgs is, often times, the only useful resource
- Due to the complete lack of useful documentation on various packages, one typically needs to browse nixpkgs in order to know the configuration options which are actually allowed.
Go all the way or don’t bother
- The best way to manage anything in NixOS is to use Nix files. This means that even your vim configurations and your dotfiles should be managed in terms of Nix files. There’s a Nix-based vim package manager which works well for plug-ins without a compiled component. Still, if you want to keep your dotfiles portable, it will be much more difficult to use them within NixOS.
There is no startx
- On both Slackware and Arch, I boot into multi-user mode and manually run
startxwhen I’m ready for a graphical session. This is impossible in NixOS, due to there being no
startx. A small annoyance, sure, but it means I need to put up with some DM. Worse, the default NixOS DM, Slim, is abandoned and lacks common functionality like reading
- On both Slackware and Arch, I boot into multi-user mode and manually run
Hard to debug
- Due to how foreign and opaque the NixOS file system is, with symbolic links up the wazoo, finding out why a program isn’t running is very difficult.
I would absolutely consider using NixOS for a non-development machine. I’m very much considering moving jeaye.com from DigitalOcean to Linode, which will allow me to use NixOS. Being able to describe complex setups like my postfix + dovecot mail server, for example, using Nix files, would really give me some peace of mind as a administrator.
For those interested, there is also a newer project, GuixSD, which uses Guix, a fork of Nix. GuixSD tries to accomplish very similar tasks as NixOS; the key difference is the use of Scheme instead of Nix’s purely functional language. GuixSD is much younger and supports fewer packages. Furthermore, since it’s part of the GNU project, you can expect that it would be very partial toward free software.