A Year with NixOS

Jethro Kuan
4 min readSep 23, 2018

--

I’ve been a Linux user for as long as I can remember (which goes up to 5 years now), and have experimented with many distros. I started out with Ubuntu and Fedora. I then migrated to Arch-based distributions like Manjaro, and back to vanilla Arch. Slightly over a year ago, a friend of mine convinced me to give NixOS a try. After a period of struggle, and 500 build generations later, NixOS has really grown on me.

What is NixOS?

NixOS is a Linux distribution which takes a unique approach to package and configuration management. Everything, down to the kernel, is built by the Nix package manager with a declarative functional build language. To the lay-user, this means that we get:

  1. Atomic Upgrades: any changes are always revertible. Broke your system during an upgrade? Simply boot into the previous generation. This was a huge lifesaver several times, and would’ve been really handy while I was still on Arch.
  2. Full Reproducibility: I run NixOS on both my desktop and laptop. The configurations I use are almost identical for the two machines, less some driver installations. My setup is replicated across both machines. As an anecdote, with the combination of NixOS and stow (to manage my dotfiles), I setup my laptop within 30 minutes of a fresh install of NixOS.

Why NixOS stuck

My full OS setup is simple: I only require Emacs and Firefox. Even then, I’ve found that when using other Linux distros, the state of the system slowly but surely deteriorates into a spaghetti-like mess. In NixOS, only sh and /usr/bin/env is available by default. Everything else is declaratively added into your root system. This way, I’m always aware of exactly what I have available on my system.

I do all sorts of OS experiments, like changing window managers. NixOS makes this a trivial one-line change. For example, when I experimented with several window managers, I tried out exwm, stumpwm, xmonad and ratpoison , all with just one-line changes:

services.xserver.windowManager.xmonad.enable = true;
services.xserver.windowManager.stumpwm.enable = true;
services.xserver.windowManager.exwm.enable = true;

If I don’t like the changes, I can easily revert my system state. Knowing that I’ll always have a build generation to boot back into allows me to experiment with peace of mind otherwise unattainable.

Declaratively specifying systemd services and timers also simplified my setup. For example, I use mbsync to fetch my email, and run it using systemd on a timer every 2 minutes.

Declaratively adding systemd services and timers

I install the required packages, and ensure that executables like pass are available to the systemd service trivially through this declarative interface.

Programmatically editing content in my hosts file was also so simple. I fetch a text file from Github and write it into my hosts file:

Fetching a text file from Github and adding the entries to the hosts file.

Maintaining my operating system has never been simpler!

Development with NixOS

As a student and software engineer, I spend a lot of my time writing and running code. NixOS allows me to have project specific dependencies, and ensures that my development environment is consistent across all my machines.

Using nix-shell, I’m able to create a development environment containing only the dependencies required. For example, I create a python environment containing only Python and some libraries with the following shell.nix file.

A shell.nix file for my machine learning research project.

I use nix-shell with direnv, so within Emacs and my shell, my executable paths and environment variables are set correctly when I’m working on a project, and when I switch projects.

Pain Points

When I first started using NixOS, documentation was sparse. It took me a long time to get used to the Nix language, and even after a year of use, Nix is still rather foreign to me. I faced a lot of difficulty initially trying to get my wireless dongle to play nice with NixOS (which ultimately required kmod and some hackery), and writing shell.nix files for setting up development environments.

Even when using a shell.nix file, the development environments are also only fully replicated if nixpkgs, the repository specifying how packages are built, is pinned to a particular commit. This requires large amounts of space, which my desktop with a 128GB SSD cannot afford. In fact, because NixOS stores multiple generations and builds packages from specified dependencies, it is not uncommon for the nix store, which stores all built packages, to quickly grow in size. Fortunately for most people, storage space is hardly an issue. I live with it by keeping my operational environment minimal, and garbage-collecting frequently.

In addition, Nix does not follow the Filesystem Hierarchy Standard (FHS), which some applications do assume. This can be emulated with some Nix utilities, but this means extra work when trying to install and use applications. Many packages in Nixpkgs are manually patched so they can work in Nix.

If following the unstable channel of nixpkgs, which receives upgrades regularly, it is also common for a upgrade of the system to fail. Because Nix tries to only keep a single version of the same package in nixpkgs, upgrading this package could mean other packages depending on an older version of this package would fail. This is common in language-specific packages, such as Python. Hence, to use Nix properly as a development environment, I have to write my own derivations for packages that are broken. However, this is only because I only use nix to specify that I require packages, and not the versions of the packages I require.

Conclusion

NixOS is definitely a difficult operating system to get into, but it was well worth the effort for me. Through using NixOS, and reading the build specifications for packages in NixOS, I learnt a lot about the Linux itself. Now I have an operational system that I can replicate and rely on, which can last.

--

--

Jethro Kuan

Software Engineer in the making. I write about my OSS work, Emacs, Deep Learning and other things.