As a freelancer, I put my trust in NixOS. Here's why.
Sebastian Staffa
Published on Nov 7, 2024, 11:11 AM
When I started freelancing some time ago, I got myself a Framework16 Laptop and I didn’t have to think twice about the operating system I wanted to use. At this point, I had already been using NixOS as my daily driver for more than a year and I had no hesitation building my business on that system. Today, I want to talk about the reasons why I put my trust in NixOS.
About: Nix & NixOS
First of all: This is not a tutorial. If you want to get started with the Nix ecosystem, there are many resources out there that can help you.
If you just want to understand what I am talking about, here's a short summary: Nix is a software ecosystem that includes of a functional DSL, a package manager called Nix (which can also be used standalone), and a Linux distribution called NixOS built using these two technologies. It aims to provide a declarative, reproducible way to define builds for software projects of any kind by specifying dependencies not as black boxes but as additional Nix expressions that can be (re)built at any time to create the needed binaries.
Reasons
What follows are my top three arguments for using NixOS as a freelancer. They are not about the beauty of the system or the elegance of the language, but about the practical advantages that Nix and NixOS give me in my daily project work.
Separation of Project Dependencies
As a freelancer, I work on multiple projects simultaneously for customers with vastly different project setups and languages. Especially in closed-source Python and C/C++ projects, build scripts sometimes assume that a project is the only one a developer is working on, requiring globally installed libraries (hello Boost, my old friend) and binaries.
Nix uses a concept called "Nix shells" which allows a user to launch a shell session with all environment variables (and especially the PATH variable) set so that only the specified dependencies in the specified versions are available. For all tools that run within this shell, everything appears as if installed globally. Since shell construction relies purely on environment variables, multiple shells can be open simultaneously and switched between without any problems. Additionally, all of these dependencies are immutable, or read-only, which means that they cannot be changed or updated by any process running inside the shell.
This feature can be combined with community tools like
nix-direnv, which automatically
sets up the Nix shell when entering a directory with a shell.nix
file, or
dev-env, which extends the built-in dependency management
with features like process/service or git hook management to express a complete
development environment in a single, declarative file.
Creating a unique shell for each project that I am working on allows me to keep my system clean while enabling scripts that assume global installations of dependencies to run without modification, which, in turn, cuts down on the time that I need to set up a new project or switch between existing ones.
Universality
Oftentimes, when I talk about Nix, people respond with a different tool they use - "X can do that too" or "Y is much easier to use." Most of the time, they're even right. The beauty of Nix is not that it can do something, but that it can do everything. As a freelancer, this is massive because it reduces the number of tools I need to master.
A recent example from my work was the Python build system
Poetry, which I hadn't worked with before.
Additionally, the project relied heavily on binary dependencies (since it was an
ML project). Without Nix, my first task would have been to figure out how to
install these binaries cleanly without polluting my system, understand how to
use Poetry (likely installing it globally), and so on. But as a Nix user, I
could search for nix poetry
and find the
poetry2nix project. With this
tool, I could reframe my problem: instead of a Poetry problem, I now had a Nix
problem. And Nix is something I know a bit about.
Another example is the very laptop I'm writing this on. Traditionally, I judged
Linux distributions by their hardware support and how much manual setup was
necessary. With NixOS, once again, a community project comes to the rescue:
nixos-hardware. nixos-hardware
maintains a repository of hardware fixes and configurations that can be included
in your NixOS configuration. Again, Nix lets me reframe the problem: instead of
searching the web for the correct udev rules for my fingerprint reader or
patches to fix some
Bluetooth quirks,
I only need to update my NixOS configuration.
Reproducibility
The Nix build system strives to achieve bit-reproducible builds across time and space. This might sound like something only relevant for security professionals wanting to ensure that their compiler binaries are free of backdoors, but the "time" and "space" aspects have practical applications in a freelancer's daily life.
Have you ever tried to return to a project that you haven't worked on for a long time and found that it broke seemingly without reason? Maybe you ran some system library upgrades (f.e. glibc) or updated your global script interpreter (f.e. node or python) and the projects that you were working on at that time chugged along without issues. Now imagine that a client requests a small change or security fix a year or two after you last touched this client's project and you find yourself in such a situation. As a freelancer, you now have to decide whether you take the hit and fix your setup in your own time or whether you potentially anger the customer by billing for work that exceeds their expectations. With Nix, you can be confident that your dependencies haven't "rotted away". This is due to two reasons: firstly, project dependencies are separated (see above) and cannot be replaced globally while you work on a different project. Secondly, Nix has a feature called pinning, which lets users "pin" a specific state of the Nix package repository (called nixpkgs) to a specific commit. Using this pinned state, along with the ability to rebuild all dependencies from source, older dependencies can be rebuilt and used in a project, even if original download links are dead. Granted, rebuilding everything might take time, but this process can run in the background while I do other stuff, which helps me keep the cost of fixes low.
Reproducing builds across space (i.e., machines) is even more valuable,
especially if this build can reproduce your entire setup, including all
installed tools, dotfiles, and even fonts. This is possible with NixOS's
configuration file, which can be versioned, shared across machines and even
rolled back as every change to the config creates a new "generation" which can
be selected during the boot process. As a freelancer, this gives me peace of
mind regarding my setup: if I break my system, for example by upgrading drivers
or playing around with a different window manager, I can easily roll back to a
previous NixOS generation. In the worst case, if my laptop is stolen or breaks,
this even enables me to set up a completely new computer to get back to work in
about an hour (assuming I have new hardware at hand). I just need to install a
blank NixOS on the new machine, clone/copy my configuration repository, and run
a nixos-rebuild
. If I have another machine with Nix, I can even create a
live image from my existing
configuration and use it to install on the new machine.
Downsides and the Learning Curve
First of all: Nix is a tool for nerds, and will probably stay like this for a while. It is missing any configuration GUI to speak of and the few community projects that attempted to add still have quite a way to go. This means that you have to be comfortable configuring your system (or rather: programming your system) using a text editor and the command line. Nearly every pattern that is used in other distributions won't work in NixOS because of its immutability, from configuring system services to installing fonts, one has to learn how to archive the desired outcome using Nix.
The Nix ecosystem itself doesn't come without its flaws either, the biggest one being the learning curve even for those who are determined to get into it. Firstly, it comes with its own functional programming language, which confuses many newcomers. Secondly, the ecosystem has moved quickly in recent years, and many tutorials and blog posts are outdated. Even though they still work as Nix is quite good at backwards compatibility, particularly the split between "traditional" Nix derivations introduced a lot of alternative ways to do things, which can be confusing at times(at least it has been for me). Lastly, although it has improved recently, the official documentation is still not where I go to when I need to look something up. Most of the time, for me, the best resource is the nixpkgs repository to find out how others have solved problems similar to the ones I need to solve.
Conclusion
Even with all these downsides, I am still using it. Not only that, but as a freelancer I have even built my business and ultimately my livelihood on it. It might be difficult to get into, it will repay every minute you put into it many times over when you can just run whatever you need to run, independent of space and time.
What I mean by this might become clearer when I tell you about how I set up my Framework16: I unboxed it, booted a live image, cloned my NixOS config repo, rebooted into my new old system for the first time and everything was like it had been on the machine that I had worked on for the last year.
Credits
- Cover image by Hide Obara on Unsplash, edited by me
- The NixOS logo used in the cover image is licensed under the Creative Commons Attribution 4.0 International License