I've always had a bit of a "problem" where I choose to wipe my local workstation to get a clean slate and/or try something new. It had gotten to the point where, for a stretch of 5 years or so, I don't think a single operating system install lasted more than a year on my system. With those reinstalls came reconfiguration, of course. Some of it was what I was excited about, hence the reinstall usually, but much of it wasn't. Spending the first few weeks slowly getting my environment working across all my development projects was a real downside.

Dependency Hell was a real issue I faced several times. Every time it was Python libraries that I installed system-wide. It took me a few times to learn about virtual environments and all the real good stuff, I'm sure others can empathize with being a newbie. Sometime after learning about virtual environments, I learned about Docker containers. These were nice because they were language agnostic. Developing inside of containers never really clicked with me though, it always felt like I was hitting hurdles with getting my IDE integrated. I was almost certainly holding the tool wrong, because I saw many solutions tailored specifically to this problem, but nothing I tried in that space really jived with me. Many years later, around 3 years ago, I finally started experimenting with Nix. Nix was one of those things that always had really passionate people bringing it up, but it just sounded like too much to figure out, and honestly looked too complicated. With LLMs in hand though, the barrier started to come down.

I wish I had a single sentence description for Nix. It is many things, which is part of the reason why it solves so many different problems. It's a functional programming language, but it's really just a DSL (Domain Specific Language) that is only meant for system configuration and package management. That there - declarative system configuration and package management - is truly what makes it so powerful. Nix is also a package manager. It is a unique way to bundle software with all of its dependencies, pinned to specific versions with lockfiles, and every package in the store gets fingerprinted so it can't be substituted without you knowing. The software repositories are actually backed by git, the main example of this is nixpkgs. nixpkgs is one of the largest software repositories at the time of writing, so you have a good chance of finding software you need in there. Not only that, but people create their own Flakes to manage packages. One that I use is llm-agents.nix, for example. In this case it allows me to get new versions of things like Claude Code and Codex much quicker than they land in nixpkgs. It's really nice having that there, but packaging software for Nix yourself is easier than ever with LLMs around.

NixOS is the bundling of the DSL and the package manager as its own Linux distribution. You manage your entire system configuration declaratively. It's pretty great: when you remove a line defining a package from your configuration and rebuild, you no longer have that package. But at the same time you do, because it keeps past builds available for you to roll back to if needed. The number of generations kept around is configurable, of course. I could go on and on, the Nix Store is an incredibly clever way to manage software installations.

While distro hopping, I ended up trying NixOS, which immediately hooked me. One reason I like to rebuild my systems is that I feel like I very quickly develop bloat. I'd have many packages installed that I found useful at one time, or thought would be useful, that I didn't end up using after some time. Rather than cleaning my home, I guess I'm the type to just buy a new one. NixOS is different. It was like being able to always have everything in my home where I expect it to be, even if I were to move houses. If there's software that I'm not using anymore, it's very visible because I'm looking at my package definitions frequently when installing new software. I have all sorts of dotfiles (custom application configuration) that I've accrued over the years, and managing those can be bundled right into the rest of my system configuration, alongside the code that actually installs said package. To make a long story short, I've been on NixOS for my personal workstations for over 3 years now.

Using NixOS is not an option for my work computer though. Bummer. I work on a Macbook. Nix does support MacOS though! It's a bit more limited when it comes to system configuration (one can look into nix-darwin to see what's available there). It is still great for managing package installations, even Homebrew! It also can manage my dotfiles just fine. This is where I really feel like I've unlocked a new ability, because I have a modular configuration where I define configs that I want on both systems in one place, and they both reference that. Improvements I make while on my personal computer make their way to my work computer on the next rebuild, and vice-versa. No more changing dotfiles on one computer and realizing I forgot to commit it to my dotfiles repo and now I don't have it on my other computer. Every change I make is version controlled. I don't even give myself the option to skip Nix for changing a config file managed by it, because Nix makes it read-only. This does slow down config changes to some degree, but I think overall I'm spending way less time managing configuration because of the easy portability.

Not only that, but I can pair Nix Flakes with direnv, and more specifially nix-direnv to have a wonderful development experience on my different projects. By having a flake.nix defined per-project which defines my dependencies for the project, paired with direnv to automatically activate the flake when I enter the project directory, I'm able to isolate dependencies and configuration to projects very easily. Dependencies don't even have to be from nixpkgs, there's all sorts of tooling people have made. A great example is uv2nix. There's so many possibilities. I've used a hierarchical directory structure to have a flake.nix defined for different teams I was on at work. When I was in the directory containing infrastructure repos, it would activate that flake and have their standard tooling available for all the repos. Adding a flake to every repo wasn't really an option because I was the only person on the team using Nix. Nix flakes can also manage pre-commit installs for projects.

Am I satisfied with this solution? Absolutely, in the sense that I really don't think there is anything out there that would work better for me right now. I do think that Nix is very esoteric, and don't see that changing, even over time. I think it has the right foundation, and ideas for solving these problems, but I don't think it is an implementation capable of going 'mainstream'. Without LLMs, I'm not sure I'd have any Nix setup at all, because the learning curve is more of a cliff at the start. I do think that once it clicks, the curve flattens out near the top. That initial hurdle is exactly what prevents it from taking off.