It Works: Running on NixOS

Well, that was an ordeal. It took the better part of a week, but I have a server again.

This blogging platform was not part of the problem. It was a joy to work with. Hats off to the people at Ghost

The reason it took so long to setup is that it's running on an OS from the future. It's called NixOS, and some of the time it's really great.


The headline feature for this operating system is that it uses Nix, the purely functional package manager. With Nix, all packages are given unique names that include a hash of all their inputs, including not just their sources but also the compilers used, the build scripts, and the rest of the environment. Inputs that are not explicitly specified are not present for the package build process, so it's really hard to create hidden dependencies. It's kind of like deb or rpm, but a little better about tracking dependencies.

Its approach to deploying those packages is unique. Rather than install packages in place, it stashes them all in the Nix store (located at /nix/store) and uses a forest of symlinks and $PATH entries to put them together into a useful combination. Want to use a new version of Python for a little while, without reconfiguring your system? Just invoke nix-shell with the proper incantations and it will build you a new environment and start a shell in it. With a system like this, package rollbacks are pretty easy, too.

If you're a Haskell programmer and you haven't heard of Nix before, you may be salivating right now. That language has an unforuntate combination of being finicky about package versions, and a tooling system that's not very good about installing two versions of the same library side-by-side. Nix's features will be of useful for lot of people, but Haskell programmers have more reasons to be excited about it.


All that's pretty neat, and an operating system based on Nix would be interesting in of itself. But NixOS goes beyond that starting point.

NixOS puts itself in charge of building /etc and other config paths based on a single configuration.nix file. It does this using the same domain-specific language as the Nix package manager. The language has all the usual features, like functions, variables, and importing other files. It turns out this is a great idea.

I never noticed it before, but there's a lot of redundant work in configuring a typical Linux box. Ports and user names wind up in several different config files, and the system doesn't work if they don't line up. It's much easier to manage these things when a port number or user name can be specified as a variable which can then be pulled into several different config files.

Although the end result is an /etc that should look familiar to most admins, the work of formatting the Nix configs into the usual file formats is well hidden. That's another benefit of using a programming language for this; all the logic for writing these files can be handled in a library. The admin sees a single uniform interface for configuring everything.

Almost everything is written to be easy to use. Here's one of my favorites:

ghostContentPath = "/var/ghost/content";
services.tarsnap.enable = true;
services.tarsnap.config.ghost.directories = [ ghostContentPath ];

Those three lines enable tarsnap daily backups for this blog!

There's lots more goodies like this in the NixOS Manual's Appendix.


Finally, there's a tool that ties this all into the cloud. This makes it easy to spin up an EC2 or GCE instance that matches that specification. Or you can build a template and stamp out a dozen different instances, with each one parameterized slightly differently. When the machine specifications are just expressions in a programming language, they can be copied, modified and instantiated easily.

Deploying changes is easy, too. Just update the config files and run nixops deploy. The program will take care of starting or stopping machines, installing packages, and restarting services.

At this point, it starts to encroach on Puppet and Chef's territory. Which is a good thing, since those tools seem to be regarded as (at best) a necessary evil.

My Experience

The down-side is that actually making these packages can be a bit of a pain. It's a young project. There's very little documentation or examples, so you're on your own once you've strayed from the well-traveled path. The Nix interpreter's error messages are often confusing. Perhaps worst of all, most editors don't have syntax highlighting for .nix files yet.

The reason it took me so long to build this site is that I had to build a bunch of my packages from scratch without much help from documentation.

Most of my work is too crude to be upstreamed into the nixpkgs tree, but I can still post it online. Maybe this can save someone else some time.

Show Comments