TL;DR
Nix ends system chaos and “dependency hell” through declarative configuration. All packages are stored in isolation within the /nix/store, preventing version conflicts and enabling instant rollbacks. Despite a steep learning curve, it’s the ultimate solution for full system control.
System Rot und Dependency Hell
I started using NixOS to avoid dependency hell and, above all, the chaos on my system. At some point, I simply lost track of what was actually installed and how things were connected. Configuration drift was also an issue, but primarily I wanted to regain control. In the beginning, I first tried to understand how it even works. Instead of managing everything just via dotfiles, NixOS offers a central configuration.nix. In a way, my entire system is explained within this config. Nix is a declarative language. So, I define what the final state should look like.
But Nix is also a package manager and runs on Linux, macOS, and via WSL2 on Windows. That’s why I want to go into more detail here.
In the past, I used Brew on macOS or APT, Flatpak, and Snap on Linux, which allowed me to install applications quite easily. If I needed a program quickly, I just installed it. Sure, I could create dotfiles and enter everything meticulously – but I wasn’t forced to do it, and in those moments, my chaos was often stronger than my sense of structure. Eventually, I simply didn’t know anymore what I had “quickly installed” three months ago. This is essentially impossible with Nix! (Or at least it breaks the concept, as I would otherwise unnecessarily bloat my system with Brew and the like. Ultimately, it’s my drive for order and structure that pushes me.)
In any case, using Nix more or less forces me to write the programs and tools I need into a configuration -> configuration.nix. Everything can be entered there. Nowadays, I tend to use the standard of writing it in a flake.nix. The syntax is slightly different, but I’ll go into that later.
Now that I have a configuration, I can build it with a single command:
# Example for my system rebuild (NixOS with Flakes)
sudo nixos-rebuild switch --flake .
Nix then installs the required programs into the /nix/store based on this definition. What I find great is that different versions can coexist peacefully, e.g., for different users or my own projects. All programs are isolated from each other and are connected to my respective configuration via symlinks.
Example #1: Project Isolation
- My Project A: Python 3.11, databricks-cli 0.18.0
- My Project B: Python 3.14, databricks-cli 0.27.0
Since all these programs are installed within the /nix/store and isolated from one another, I no longer have dependency problems. No more “breaking” required packages through Homebrew during an update.
I also find it advantageous when different projects of mine need the same packages. Instead of installing the packages twice, a symlink is created that points to the unique path in the /nix/store.
For the Nerds (Deep Dive)
Technical background on the stability of the system:
- The Store Hash: Every path in the
/nix/storecontains a cryptographic hash of all inputs (source code, dependencies, build scripts). If even a tiny detail of a library changes, the package gets a new path. This is the basis for isolation. - Profiles & Generations: Nix doesn’t just delete old versions. It creates “generations.” During a system update, a new profile is created. The symlinks then point to the new store paths. In case of an error, a rollback simply points the symlinks back to the previous generation.
- Sandboxing: When building a package, Nix does not allow access to the internet or other parts of the system that were not explicitly defined. This ensures “purity” – a package builds exactly the same today as it will in two years.
- Hardlinking Optimization: Although everything is isolated, no unnecessary space is wasted. With
nix-store --optimize, Nix identifies identical files across different package versions and stores them physically only once (via hardlinks).
Advantages:
- Transparency: I know exactly what is installed on my system at any time.
- Reproducibility: My setup is identical on every machine.
- Rollbacks: If an update causes trouble, I simply jump back to the last generation.
Disadvantages:
- Learning Curve: Nix was quite stubborn and complex for me in the beginning.
- Disk Space: My store grows quickly if I don’t regularly use garbage collection.
Conclusion
Nix is a real hurdle at the start. The language is peculiar, and the debugging is often horrific for me, as the error messages don’t really tell you what’s wrong. The documentation exists, but is partly outdated and buggy. Much of what I learned about Linux systems, I had to throw overboard.
Also, I have to do a build after even the smallest change, which takes time, instead of being ready immediately after the change, like with stew, for example. Sometimes it feels to me like a trip to a government office.
But the feeling of knowing exactly what is on my computer, and the certainty of being able to reproduce everything at any time, is absolutely worth it from my personal perspective. Since I’ve made friends with the idea that my system is a configuration, any other form is pure chaos to me.
Would I recommend it? As an operating system, I have to honestly say that it has a huge overhead. Everything, really everything, has to be configured by myself, whether it’s sound drivers or the desktop environment. It simply all has to be in the configuration. As a package manager, however, Nix is definitely worth a look!