NixOS made me go back to Windows

I'll tell you about my experience using NixOS and why I decided to switch from Linux to Windows after 2 decades.

Introduction

I still remember that day more than 20 years ago when, as a teenager with a pedal-powered Intel 486, I went to the neighborhood Internet café where I spent many afternoons playing Age of Empires. I asked the owner if he knew how to install Linux, as I had heard that it was able to run on undemanding machines. We chatted for a while and he gave me one of those CDs that came with computer magazines. It contained Red Hat Linux 5. When he handed it to me he told me: "here, but Linux is not the panacea".

I left happy and eager to get home to say goodbye to a Windows 95 that I had to format every week. Indeed, it was not the panacea. In fact I didn't understand anything, but that experience planted a seed in me that I have been cultivating ever since. Over time I learned to use Linux and from time to time I tried a new distribution.

I spent quite a few years using Gentoo, as it allowed me to optimize resources to the maximum in exchange for compiling every package in the system. When my computer was powerful and modern enough, I started using distributions like SUSE or Ubuntu. Finally I settled on Arch Linux, where I have been for probably over a decade. It was relatively stable, modern and surely the best experience for using Linux at home.

At the beginning of 2023 NixOS appeared on my radar. It was trendy. I took a look at it and it looked very good, so a few months later, in the summer, I decided to give it a try. Why did I do it if Arch Linux was working fine? Mainly for two reasons:

  1. I like challenges and new things. Although I am not a distrohopper, NixOS seemed to offer a totally different Linux experience than other distributions, quite aligned with my way of programming. Arch Linux was state of the art a few years ago, but nowadays I would say that it doesn't matter if you use Arch, Ubuntu, Fedora... they are all pretty much the same.
  2. I felt like my Arch Linux installation was a mess. I no longer knew which files I had modified. If for some reason I had to reinstall one day, it was going to be a problem putting everything back the way it was.

But definitely, NixOS isn't the panacea either.

I used it for a few months, until I gave up and installed Windows 11 for day to day use. Yes, you read that right, my experience with NixOS has ended with the Windows installation USB. In reality I've almost always had Windows installed on another partition to play games and do other things that Linux didn't allow, although I rarely used it. And on the other hand, although I do not consider myself an expert in NixOS, I think I have reached a sufficient level to share my experience after trying my hardest.

In this article I'm going to tell you about my fruitful yet disastrous experience using NixOS and, to continue the meme, I think 2024 isn't going to be the year of Linux on the desktop either.

Satirical newspaper cover declaring the year 1812 as the year of Linux on the desktop

The good

I'm going to start with the good things about NixOS because the fruitful experience was not sarcasm. The NixOS core (Nix store) is genius and opens the door to many other tools and features. I'm going to describe a couple although NixOS is truly a great distribution.

Declarative configuration

Without a doubt this is the feature of NixOS that I liked the most. Nix is, besides a package manager, a pure, functional, lazy, declarative and reproducible programming language to express (in .nix files) the state of your system. In other words, you will not have to execute commands that you will later forget nor will you have to modify files scattered around the system. You will perform all the configuration of your system through files that you can save in Git or wherever you want.

In these files you can specify the packages that must be installed, the hardware configuration (kernel, partitions, encryption, ...), the system users, the Google Chrome extensions that will be installed automatically, the GTK theme... practically everything. Altering the system configuration will be as simple as modifying these files and changing the system to the new state. Furthermore, you will be able to modularize the configuration as you like thanks to the import function. Wonderful.

Example of declarative configuration for Firefox
  • Nix
{ pkgs, lib, ... }:

{
  # Firefox configuration: profiles, extensions, etc...
  programs.firefox = {
    enable = true;
    package = pkgs.firefox-devedition;
    profiles.felixsanz = {
      extensions = with inputs.firefox-addons.packages.${pkgs.system}; [
        ublock-origin
        decentraleyes
      ];
      settings = {
        "browser.tabs.warnOnClose" = true;
        "extensions.pocket.enabled" = false;
        "media.ffmpeg.vaapi.enabled" = true;
        # ...
      };
      userChrome = builtins.readFile ./userChrome.css;
      userContent = builtins.readFile ./userContent.css;
    };
  };

  # Environment variables associated with Firefox
  home.sessionVariables = {
    MOZ_DISABLE_RDD_SANDBOX = 1;
    MOZ_ENABLE_WAYLAND = 1;
  };

  # Set Firefox as the default browser
  xdg.mimeApps.defaultApplications = lib.genAttrs [
    "text/html"
    "text/xml"
    "application/xml"
    "x-scheme-handler/http"
    "x-scheme-handler/https"
    "x-scheme-handler/about"
    "x-scheme-handler/unknown"
  ] (_: "firefox.desktop");
}

Cool, isn't it? Keep reading, all that glitters is not gold.

Nix shell

The Nix shell is a powerful tool that allows you to temporarily modify the state of the machine. For example, you will be able to run an application without having it installed. You create a shell with that package/configuration and when you close it, it will leave no trace.

You could not have Python or Node.js installed globally, but when you go to work on a specific project, you open a shell with what you need and start working. This configuration can be stored in a shell.nix file like the following:

Example of Nix shell
  • Nix
{ pkgs ? import <nixpkgs> {}}:

# The shell will be able to use Python 3.10 and the flask, requests and yeelight libraries

pkgs.mkShell {
  packages = with pkgs; [
    python310Full
    python310Packages.flask
    python310Packages.requests
    python310Packages.yeelight
  ];
}

The usefulness increases when combined with nix-direnv, an application that will automatically instantiate the shell.nix file when we enter the project directory via cd. It will make sure that everything is available.

The community

Perhaps the most valuable part of NixOS is not the technical part, but the human one. In support channels like their Discord community or the forum you will find friendly people always willing to help, with a patience that I have rarely seen. From the first moment I felt part of the community and at all times I received help when I needed it.

The ugly

NixOS has quite a few ugly things that are supposed to be virtues, but in my opinion they are poorly executed. At least for now, I find them a source of frustration and problems.

Unlearning everything you know

Do you have Linux experience? Well, congratulations, but it's not going to be of much use to you. Creating users with useradd, encrypting partitions with cryptsetup, configuring disks in /etc/fstab, creating shell aliases, configuring systemd services... nothing is going to help you. Ideally, you will perform all system configuration declaratively. This, as I said, is what I liked most about NixOS but relearning everything all over again is a slow and paintful process. It is what it is.

Home Manager

NixOS manages system configuration but not user-related configuration. Home Manager, a project created by third parties, takes care of that. It fills a gap that should not exist. Although overall it works well and is a great solution, it has some flaws that add more frustration to system setup. The advice I used to get the most was the less I use it, the better.

Options inconsistency

In NixOS a lot of configuration is done using options, for example:

Example of configuration using options
  • Nix
{
  programs.zsh = {
    enable = true;
    dotDir = ".config/zsh";
    history = {
      path = "${config.xdg.dataHome}/zsh/history";
      extended = true;
      share = false;
      ignoreAllDups = true;
    };
    initExtra = "setopt INC_APPEND_HISTORY_TIME";
    syntaxHighlighting.enable = true; # <----- X.enable and enableX, ¿why?
    enableAutosuggestions = true;     # <----/
    shellAliases = import ./aliases.nix { inherit config; };
  };
}

The problem is that sometimes you need options that do not exist and you have to resort to manual configuration using xdg.configFile and derivatives.

On other occasions you will find options from the same package in both NixOS and Home Manager. Sway is installed via programs.sway.enable or wayland.windowManager.sway.enable? For a beginner all this is a wall. Another example, there are options for Google Chrome but not for Brave.

Not to mention all the time you spend generating this configuration, looking for the options, dealing with their inconsistencies, etc.

The options thing is all well and good but I found it to be quite chaotic and unreliable.

The bad

Now come the things that have made me waste countless days looking for a solution that sometimes doesn't even exist.

Awful documentation

The documentation is somewhere between awful and non-existent. Sorry, a poorly documented project loses a lot of value.

The wiki is abandoned and everybody recommends you not to use it. There is no documentation, you have to read the source code of the packages constantly. There are also no practical guides on how to do this or that. Also, the resources you find online (like blogs) are often outdated.

In short, it's exasperating. Thank goodness the community is very active and puts a lot of effort into helping others, but there comes a point that you feel bad for asking so many questions and sometimes you get stuck and you have no immediate help, and no way to find the solution on your own.

Just one version, really?

In Arch Linux you could install any old version of a package easily. Do you need CUDA 11.7, 11.8 or 12.1? No problem. On NixOS forget it, even if you make your own package you will need to manage all the dependencies by hand. There are only a couple of packages that are versioned (like python310 / python311), the rest... nothing. If you want an older or newer version of what nixpkgs offers, you're doomed. And if the package doesn't exist, good luck creating it. In other distributions you might be able to get by somehow, not here.

Nix shells for Python development

This has pissed me off, I'm not going to deny it. I don't even want to count the amount of hours I've wasted trying to get Python applications to work on NixOS. If you try to use venv and pip to install your project dependencies, you will start having a lot of problems with system dynamic libraries. No matter how hard you try, there is no way.

As we saw in the example above, many Python packages are found in nixpkgs:

shell.nix
  • Nix
{ pkgs ? import <nixpkgs> {}}:

pkgs.mkShell {
  packages = with pkgs; [
    python310Full
    python310Packages.flask
    python310Packages.requests
    python310Packages.yeelight
  ];
}

Both flask, requests and yeelight are available. Actually most of them are. But what do you do when the one you need is not available? Nothing. Waste days trying to get your application working. You can request the package in the GitHub repository to have someone take care of it, or try to collaborate by maintaining it yourself, but it's a very complicated task.

In this blog I'm sharing the experience I gain around Artificial Intelligence libraries, but NixOS has put so many obstacles in my way that I couldn't even do my thing. I've been waiting several months for them to publish onnxruntime-gpu, but there's no way, it's still there. It seems so complicated that nobody knows how to do it.

Besides, we're back to the same thing... just one version, really? In Python it is quite normal to install specific versions of dependencies, for example gradio==3.41.2. You can't do this on NixOS, it's ridiculous!

There was a time when I managed to get such an application working in exchange for losing 3 whole days trying. It's worth noting that these packages have to be compiled on your machine, so certain libraries took around 4 hours and needed more than 32GB of RAM, so I had to resort to swap as I didn't have enough memory. Beautiful experience, isn't it?

As if that weren't enough, weeks later when I upgraded the libraries, it broke again and I was no longer able to fix it no matter how many hours I put into it. That's when I started downloading the Windows 11 ISO.

Only one version

You can avoid updating these libraries through the pinning mechanism. In fact, if you don't do it, you will probably have to recompile libraries every day and wait hours for it to finish.

  • Nix
pkgs = import <nixpkgs> {};
pkgs = import (builtins.fetchTarball {
  url = "https://github.com/nixos/nixpkgs/archive/2c7f3c0.tar.gz";
  sha256 = "1yswzpqq384ia4k2lyi8qicp07bvk5lsdx2y7wsd3sdvj79g72za";
})

But what happens when you need to update a specific library? Well, you have to update ALL of them, since nixpkgs is a snapshot where at any given time, there is only one version for each library.

In short, if you need this workflow stay away from NixOS.

NixOS is Linux

Right now I'm at a point in my life where I want to focus on what's important and don't want to waste time dealing with the operating system. And NixOS, it's still Linux. Every "weird" thing you want to do is going to take time and fights. From pairing a bluetooth headset to formatting a hard drive. And if on top of that in NixOS it is done differently... be prepared to spend time on it. Nothing new, I've been dealing with Linux for 20 years and I know what it's like.

Self-torture

It's also partly my fault for always using the bleeding edge. For example, by using wayland and sway, you are condemning yourself to having graphics acceleration problems with NVIDIA GPUs or programs that directly do not work in wayland (such as gparted).

But for God's sake, we are almost in 2024 and this is not progressing.

NixOS Verdict

Great idea but a mediocre execution. If the documentation wasn't so poor and nixpkgs were more flexible, it would be a different story.

NixOS has a few use cases that are ideal for using it, but I think the desktop one is not the best. Still, if you are going to use NixOS as an operating system to browse the internet or simple tasks, go ahead, it's a great choice. But if you intend to do development, especially in Python, it's madness.

Back to Windows

A couple of friends who always used Arch Linux migrated to macOS and Windows around this time. Their comments were like: "this is another world, I don't know why I was still stuck using Linux". Between those opinions and my problems with NixOS, I decided to take another look at Windows.

I installed Windows on a secondary disk to test it. Within 48 hours I was reinstalling it, this time on the main drive, deleting NixOS in the process.

Windows is not what it used to be. Previously, Linux was more cutting-edge and had features that Windows could barely dream of. That's not the case anymore, I think Linux, in general, has fallen a bit behind. Windows is stable and works well. I no longer see a significant difference in resource consumption compared to Linux. Everything is much easier and it seems that privacy is now respected more than before (at least it's not so invasive anymore).

Windows has adopted many Linux practices. Apart from WSL which complements Windows perfectly, now Windows has its own package manager called winget. It works really well, I have had no need to use Chocolatey or Scoop.

Regarding the declarative configuration that NixOS offers, in Windows we can achieve something very similar using PowerShell commands and the Windows registry. In a PowerShell script you can automate both system/user configuration and application/package installation.

As for the availability of applications, luckily I have been using applications written in Rust for a long time such as starship, fd, bat, ripgrep, eza or delta. They all work on Windows as Rust is cross-platform. All the console applications that I used to use in Linux are also available here without using the WSL, what more could you ask for?

And speaking of console, Microsoft has created something amazing called Windows Terminal. I love it. It's fast, modern and has all the features you could ask from a Terminal application.

Screenshot of Windows Terminal displaying multiple tabs and running winfetch

Another thing I missed was a decent file explorer. On Linux I used Thunar, Nemo or PCManFM, but these prehistoric applications gave me a variety of problems. PCManFM, for example, would delete files when I moved them to another folder.

Special mention also goes to AltSnap, a fantastic application that mimics part of the behavior of window managers like sway or bspwm. It allows you to move and resize applications by using a key and dragging with the mouse.

The things I liked about Windows don't end here. I could also highlight the native Snipping Tool application, which works much better than the alternatives available on Linux (more hours down the drain, especially in wayland). Or the Microsoft PowerToys. Or AutoHotkey.

In short, my journey through Linux has been a great experience in which I have learned a lot, but if things don't change enough I don't know if I'll come back. Seems I'm getting old...

You can support me so that I can dedicate even more time to writing articles and have resources to create new projects. Thank you!