I think it's time to give Nix a chance

2025-05-2615:56148151maych.in

An honest look at why Nix's complex but powerful approach to package management and reproducible environments is worth considering.

The modern developer tooling ecosystem has exploded with choices, leading to frustrating scenarios where some piece of code builds perfectly on someone’s system, runs flawlessly in production, but mysteriously fails to build for you and you have absolutely no idea why. You’re left debugging with no clear direction—perhaps it’s a missing system dependency, a subtly different library version, or some environment variable that exists somewhere in the void, and nowhere else.

If this sounds familiar, you too might be experiencing the fundamental problem that Nix was designed to solve: the lack of true reproducibility in software development.

Despite being around for about two decades, Nix has largely flown under the radar of mainstream development. Most developers have heard of it in passing—often described as “that functional package manager with a steep learning curve” or “the thing NixOS uses”—dismissing it as academic or overly complex. I believe this perception is getting increasingly outdated, Nix deserves a chance.

First, let’s get this out of the way: Nix still does have a steep learning curve. It requires learning a new functional programming language, understanding unfamiliar concepts like derivations and the /nix/store, and rethinking how package management works. The documentation can be dense, scattered, and the error messages are outright unhelpful in some cases. You’re essentially learning an entirely different approach to software deployment and environment management.

But here’s the thing—as of this post, the tooling around Nix has matured significantly, and the problems Nix solves have only become more pressing. If you’ve ever lost hours debugging environment differences, juggled multiple version managers, or struggled with reproducible builds, Nix addresses these pain points at the architectural level rather than through workarounds.

And I am here to argue that despite the quirks and learning investment, Nix’s benefits are compelling enough to warrant your time. The question isn’t whether Nix, as a tool and a language, is complex—it is. The question is whether the problems it solves are worth learning something genuinely different. In this post, you can expect a brief introduction to what Nix, the tool, can do for you and how it may be worth giving a try right now.

Why Traditional Package Management Breaks Down

Most package managers work by installing software into shared system locations. Install a specific version of Python, and it goes straight into /usr/bin/python. Need a different version of Python for another project? You either overwrite the first installation or create complex alternatives systems that are painful to manage.

This shared-state approach creates inevitable conflicts:

  • Your frontend application needs the latest version of NodeJS, but a legacy service requires an older one.
  • Your applications depend on conflicting versions of OpenSSL libraries.
  • Teams use different operating systems with slightly different utilities. Heck, even same versions of tools, like sed, are functionally different across Linux and MacOS.

In such cases, one may say that version managers help with language runtimes. But what about system libraries, databases, or compiled tools? You end up juggling multiple such tools, each with different commands and behaviors.

Orchestration and containerized solutions like Docker and Kubernetes help, but they introduce their own complexities and performance downgrades. More importantly, Docker containers themselves aren’t reproducible—running apt-get update or pip install requests at different times can yield different results, even with the same Dockerfile. And frankly, no one really needs Kubernetes, they just have it because everyone and their grandma has it. I digress, that’s a topic for another day.

This is where Nix comes in.

…And How Nix Solves the Problem

Nix makes no assumptions about the global state of your system and takes a fundamentally different approach. Instead of installing packages into shared locations where they can conflict, everything goes into the immutable /nix/store, with each package getting a unique directory based on a cryptographic hash of its build inputs.

/nix/store/2v66xkgfmdipzpwgl813n4mqgck6w3fd-nodejs-22.14.0/
/nix/store/2znhzcp5ran8q5mzyqgz6lxi3a56rgva-nodejs-20.18.1/
/nix/store/4rk85a5rsladhcc3ffpnx2kwglvs0i-nodejs-18.17.0/

These hashes are computed using SHA-256 over the package’s complete build dependency graph—source code, compiler version, build flags, dependencies, even the build script itself. Change any input, and you get a different hash, which means a completely separate package in the store.

Cryptographic Guarantees and Safety

Technically, you might state that hash collisions are possible with any cryptographic hash function, and you won’t be wrong here. However, the probability of a SHA-256 collision is approximately one in 2128—or roughly one in 340 undecillion!. For perspective, this is far less likely than being struck by lightning while simultaneously winning the lottery multiple times.

More importantly, Nix implements robust failsafe mechanisms. It uses NAR (Nix Archive) hashes, a deterministic format that canonicalizes source trees by normalizing timestamps, file permissions, and directory ordering. Unlike traditional TAR archives which include non-deterministic metadata, NAR hashes ensure identical content always produces identical hashes. Nix validates packages using both the NAR hash and additional metadata like Git revision hashes, providing multiple layers of integrity verification. This has prevented an issue in the past, where GitHub changed the hash format of their archives, causing systems and services depending on these hashes to fail. Hash Collision - Flox

This isolation means multiple versions coexist without conflicts. Your React app gets Node.js 18, the legacy API keeps Node.js 16, and they exist in completely separate filesystem namespaces.

Now, I’m in no way trying to proliferate a 15th competing standard here that solves all package management woes for you and your grandma. Nix is far more capable at things than just that, and I’ll tell you why.

Reproducible Environments with Flakes

Modern Nix organizes projects using “flakes” Nix flakes - Zero to Nix —standardized specifications that pin every dependency with cryptographic precision. Think of them as package.json or Cargo.toml, but with mathematical guarantees that every single build will result in the same derivation, no matter when, where, or how you build it.

While flakes have been “experimental” for quite a long time now, it has truly pushed Nix one step closer to complete reproducibility and shared development environments. For clarity, Experimental does not mean unstable, as flakes have practically been “stable” since 2021, despite the experimental tag. Here’s what a simple flake might look like:

# This is a nix flake environment running
# Python 3.11, with pandas and numpy installed.
# The environment can be accessed by running:
description = "Example Python Data Analysis Environment";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs }: {
devShells.x86_64-linux.default =
nixpkgs.legacyPackages.x86_64-linux.mkShell {
buildInputs = with nixpkgs.legacyPackages.x86_64-linux; [

Let’s take this as an example. When you share this flake (along with the flake.lock) with someone and they build it, they get exactly—not approximately—the same environment as when you created the flake. The automatically generated flake.lock The flake.lock file - Zero to Nix file contains cryptographic hashes for every dependency in the transitive closure. This means that building this same flake over and over again, even a few years down the line, will ideally still result in the exact same environment. Well, at least until the source of the software disappears from the face of the earth, and the Nix binary cache contracts bit-rot.

Also, these Nix flakes are backed by git and only include tracked files in builds, ensuring forgotten local files and hash changes cause immediate build failures rather than silent inconsistencies.

Running Packages Without Installing

Usually to run a package on any system you would need to install it, or use an AppImage, Flatpak, snap, you name it. But not with Nix. One of Nix’s most practical features is temporary tool access without system pollution:

# Run Node.js 20 without installing it
nix run nixpkgs#nodejs_20 -- --version
# Get a temporary shell with multiple tools
nix shell nixpkgs#{imagemagick,ffmpeg}
# Try software from any Git repository with flakes
nix shell github:DeterminateSystems/fh -- fh --help

This eliminates tool accumulation while providing instant access to any software in the Nix ecosystem. When you exit the shell, the tools disappear from your environment (but stay in the /nix/store until it’s garbage collected).

True Package Isolation

Traditional package managers create a shared global namespace where conflicts are inevitable. Nix solves this architecturally by storing each package in /nix/store/ with cryptographically unique paths. Multiple versions of the same package coexist without interference because they occupy completely separate filesystem locations.

$ ls /nix/store | rg nodejs-.\[0\-9.\]+drv
/nix/store/2v66xkgfmdipzpwgl813n4mqgck6w3fd-nodejs-22.14.0.drv
/nix/store/2znhzcp5ran8q5mzyqgz6lxi3a56rgva-nodejs-20.18.1.drv
/nix/store/4rk85a5rsladhcc3ffpyfnx2kwglvs0i-nodejs-20.19.2.drv

This isolation extends beyond simple version conflicts. Each package includes its complete dependency tree in isolation, meaning you can run applications with entirely different versions of fundamental libraries like glibc simultaneously. The Nix store’s immutable design ensures that once built, packages never change, eliminating an entire class of “it worked yesterday” problems.

Simply put, each project can use a different Node.js version, present on the system, without conflicts. The hash 2v66xkgfmdipzpwgl813n4mqgck6w3fd in this case encodes not just Node.js 22.14.0, but the exact glibc version, compiler flags, and every dependency used to build it. This can be confirmed by running a simple nix-store --query on both derivations:

$ nix-store --query --tree 2v66xkgfmdipzpwgl813n4mqgck6w3fd-nodejs-22.14.0.drv
/nix/store/2v66xkgfmdipzpwgl813n4mqgck6w3fd-nodejs-22.14.0.drv
├───/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh
├───/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh
├───/nix/store/cfp8jh04f3jfdcjskw2p64ri3w6njndm-bash-5.2p37.drv
│ ├───/nix/store/3jmwf7n7mdjk99lbwmznwkjvd5kwxlp4-glibc-2.40-66.drv [...]
$ nix-store --query --tree 2znhzcp5ran8q5mzyqgz6lxi3a56rgva-nodejs-20.18.1.drv
/nix/store/2znhzcp5ran8q5mzyqgz6lxi3a56rgva-nodejs-20.18.1.drv
├───/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh
├───/nix/store/s63zivn27i8qv5cqiy8r5hf48r323qwa-bash-5.2p37.drv
│ ├───/nix/store/qhdvi3qcn60vrapyhsxxpbw0q63gmfz8-glibc-2.40-36.drv [...]

Package directory names correspond to cryptographic hashes that take into account all dependencies, build flags, and even compiler versions. This content-addressable storage means identical inputs always produce identical outputs, making builds truly reproducible across different machines and time periods.

Development Environment Excellence

Beyond core benefits, Nix’s ecosystem provides sophisticated tooling for seamless workflows. With nix-direnv nix-direnv - GitHub , an extension to direnv, Nix flake environments activate automatically when changing directories:

Now cding into the directory automatically loads the Nix environment. The cross-platform consistency is particularly valuable—the same flake.nix that works on Linux works identically on macOS through nix-darwin, and WSL, eliminating platform-specific tooling differences.

And if you still don’t want to interact with or write Nix files directly, there’s tools like Flox, Devshell, Devbox, built on top of Nix that abstract the pain away.

Security on Nix

Nix’s unique architecture provides security benefits that extend beyond reproducibility. The immutable nature of the Nix store and its departure from standard Linux filesystem conventions create inherent security advantages.

Immutable Package Store

Once built, packages in /nix/store cannot be modified. This prevents entire classes of attacks where malware modifies system binaries or libraries. Traditional package managers allow in-place updates that can be exploited, but Nix’s atomic model makes such attacks impossible.

Package Sandboxing and Anti-Tampering

By design, anyone is free to contribute packages to the central Nix package repository. Although all packages there, not marked as nonfree, are built from source, where the source is downloaded before the build process, verified by hash, and only then processed.

By philosophy, Nix prevents uploading pre-built packages to nixpkgs and requires additional reviews before merging, thus ensuring an additional layer of safety at the expense of the latest package updates being a bit delayed—usually only by a couple of days. All built packages, on the official build infrastructure or locally, are sandboxed by default. None of the packages have internet access inside the build environment, meaning that all the dependencies must be resolved before the build runs.

Also, all packages are required to contain a hash of dependencies, ensuring that the build fails if the source or any dependencies are tampered with or poisoned.

In contrast, traditional package management systems rely heavily on trust. As an example, the AUR is a repository of community-contributed Arch packages where you’re trusting random maintainers. Anyone can upload a PKGBUILD that could download and execute arbitrary code during installation. While you can inspect the build script, many users install with yay -S package without review. Similarly, the Fedora RPM repositories which have pre-built binaries signed by maintainers, where you have to trust that the binary matches the claimed source code and the build environment wasn’t compromised.

Non-Standard Filesystem Layout

Nix deliberately breaks from the Filesystem Hierarchy Standard (FHS). There’s no /usr/bin filled with system binaries, no /lib or /usr/lib containing shared libraries. This means malware designed for traditional Linux systems often fails because it cannot locate expected system components at standard paths.

Ad-hoc binaries downloaded from the internet cannot run on NixOS without explicit configuration. Traditional Linux systems allow dynamically linked binaries to access system libraries through standard paths like /lib64/ld-linux-x86-64.so.2. On NixOS, these paths either don’t exist or point to controlled implementations. This prevents many categories of malicious binaries from executing.

Controlled Binary Execution

Although not impossible, to run external binaries, you need tools like nix-ld nix-ld - GitHub which provides controlled access to a compatibility layer. This forced deliberation makes it much harder for malicious software to execute unnoticed.

While this isn’t “security through obscurity” (the design is well-documented), it does mean that common attack vectors simply don’t work in a Nix environment, providing defense-in-depth against malware targeting traditional Linux systems.

Performance and Caching

Nix provides performance advantages through sophisticated caching and deduplication. When each package is built, it is stored with its content-addressable path, meaning identical dependencies are built once and shared across all projects.

Binary caching eliminates most compilation time. Popular packages are pre-built and cached, so you typically download binaries rather than compiling from source. There are also services like Cachix, attic that can host binary cache for you so you can push and cache the lesser known, or even your own Nix packages after building them once.

With cache, the first environment activation might take up to a few minutes to download dependencies, but subsequent activations are near-instantaneous. However, environment load times can be a trade-off. Directory changes that trigger environment loading through nix-direnv can take a few seconds depending on dependencies, as Nix maintains separate instances for each tool. But in most cases, everything will be seamless.

When Nix Makes Sense (And When It Doesn’t)

After reading this far, you might be wondering if Nix is right for your situation. But what I talked about in this post is barely scratching the surface of what Nix is capable of.

Nix provides the most value when:

  • Environment drift is costly: Financial services, healthcare, or any domain where debugging production issues has high stakes
  • Onboarding takes days: Complex stacks with multiple databases, language runtimes, and system dependencies that require extensive setup documentation
  • Cross-platform development: Teams mixing Linux, macOS, and WSL with different package managers and library versions
  • Compliance requirements: Industries requiring reproducible builds for audit trails or regulatory compliance
  • Research and experimentation: Academic computing, data science, or ML research where reproducing exact environments is critical
  • Legacy system maintenance: Managing multiple versions of the same software for different clients or product versions

Nix might be overkill if:

  • You’re working on simple projects with minimal, standard dependencies
  • Your team already has smooth onboarding and deployment processes
  • Time-to-market pressure outweighs technical debt concerns
  • Your stack consists of well-containerized microservices with stable dependencies
  • Learning new tools would significantly slow current development velocity
  • You’re working solo on personal projects without collaboration needs

The Honest Drawbacks

I have been using Nix for about 8 years now. And while I would say that Nix is an indispensible part of my life at this point, I still do have some gripes with it and the occasional hurdles while explaining some concepts and philosophies to people. I still learn new things about it everyday, and yet feel like I know very little when it comes to Nix. Here’s what I think the main drawbacks are, simplified:

Learning Investment

The functional programming concepts and new mental models take significant time to internalize. You’ll feel less productive initially. Finding documentation or help for some issue you have might be difficult, but not impossible. Expect at least a few weeks before you become comfortable with the basic concepts. I can personally say it’s worth the effort and pain, but in the end it depends on what you want to achieve with Nix.

Debugging Difficulty

When things go wrong, error messages often reference store paths and internal Nix mechanics rather than familiar concepts. Troubleshooting requires understanding Nix’s execution model, which adds complexity to already difficult debugging scenarios.

Ecosystem Integration

Some software expects traditional Linux filesystem layouts. Proprietary tools that hardcode paths to /usr/bin or /lib require workarounds. Although Nix does have built-in utilities that help with this during the build process.

The way you look at and interact with packages and services in the system also completely changes once you adopt the “Nix way”.

Documentation Gaps

While improving, Nix documentation can be scattered. Error messages, though better than before, can still be outright unhelpful in some cases.

Storage Requirements

The Nix store grows large over time, requiring periodic garbage collection to remove unused packages. Although this can be solved by enabling the periodic garbage collector on NixOS or by running it manually.

How Do I Get Started?

If you’re interested in trying Nix, I would suggest:

  1. Install with flakes enabled: Use the Determinate Systems Installer for quick setup
  2. Start with temporary tools: Use nix shell or nix run to try software without installing. A list of all the official packages can be found here
  3. Create a simple development environment: Use nix flake init in a project directory and try writing a flake for it
  4. Add automatic activation: Uninstall some tools you depend on after setting up developer environment with flakes and set up nix-direnv to load environments automatically. Or use any of the other tools that abstract Nix for you
  5. Join the community: The NixOS Discourse is huge and welcoming to newcomers, and so is the Nixpkgs repository

And as for the documentation or general readings on Nix:

There’s also NixOS, if you would like to spend more time learning and understanding Nix better.

The software development landscape has grown increasingly complex. We manage more dependencies, support more platforms, and deploy to more diverse environments than ever before. Traditional package management approaches that worked for simpler systems are showing their limitations.

Nix offers a different path—one where environment reproducibility isn’t hoped for but guaranteed, where dependency conflicts are impossible by design, and where trying new tools doesn’t risk breaking existing setups.

The learning investment is significant, but for teams struggling with environment management, the payoff comes through reduced debugging time, faster onboarding, and more reliable deployments.

The best tool isn’t always the most popular one—sometimes it’s the one that actually solves your problems.

P.S.: This website runs on my homelab running NixOS.


Read the original article

Comments

  • By taosx 2025-05-2616:443 reply

    Nope, not yet. I tried that 3 times, once for my mac, then for my linux and once for a project. Every time I gave it a solid try found it lacking so I made a note to wait a few more years. The promise of reproducible systems is so hard to resist but nix brings crazy complexity (not all of it necessary), I'd prefer a system where their package repo has 4 packages but it makes it easy enough to me to bring other packages.

    Writing nix is like writing functions but in order to remember the arguments and fields of those arguments in another file that you can only access through your browser. Look at any nix file, and tell me where each variable is coming from.

    • By ThatMedicIsASpy 2025-05-2617:311 reply

      I have tried NixOS and the out of the box experience isn't bad, neither on my main VM nor my notebook. Everybody points you to flakes and flakes point you do use the unstable branch of nixos and that is where I say no. Then you learn a bunch of things along the way like upgrading replaces your stuff and you have to start again.

      I've moved to Atomic desktops and can't name anything I am missing. For dev stuff there are containers.

      • By pxc 2025-05-2617:39

        Flakes and the unstable branch of NixOS (the `nixos-unstable` branch of Nixpkgs) are unrelated.

        Flakes is an "experimental feature" of Nix itself, but it has shipped in stable releases of Nix for years now. There was a time when you had to build an unreleased version of Nix to play with flakes but that was a long time ago.

        Whether you use a rolling release for the package set, e.g., nixos-unstable or nixpkgs-unstable, is orthogonal to whether or not you use flakes.

        Fwiw, I generally prefer to run the unstable branches of Nixpkgs. Occasionally you hit something you have an opportunity to fix yourself, but that's not very frequent these days, even on the unstable branches.

    • By IshKebab 2025-05-2621:251 reply

      Yeah I think this is a big flaw of declarative systems. I see a Nix derivation that contains `foo: bar`. How do I know what this does? It's pretty much impossible to know without learning all of Nix, because anything can access it.

      If it was a function call you could just go-to-definition.

      Environment variables have a similar issue. It's often hard to know what they do because they could be used by basically anything at any time.

      • By sureglymop 2025-05-2623:211 reply

        I find this to be a real issue with environment variables.

        I am trying to create a tool to help see exactly where and by which program any environment variable was set/exported since boot.

        This is still in the conceptual phase but I'm looking into linux' ftrace to achieve this. Any ideas or pointers are welcome.

        • By IshKebab 2025-05-2713:351 reply

          Yeah I think you can definitely find which process sets environment variables (I think you just need to intercept execve or whatever). The real problem is finding which processes use the environment variables. That's extremely difficult, if not impossible.

          • By sureglymop 2025-05-2819:29

            Right. Iirc the environment is put on the stack of a new process shortly after. Though I'd want to do this from very early on and then track how the environment changes. I thought of ftrace because it can be enabled/configured in a kernel parameter and as such will already capture information shortly after boot. But maybe it would be easier to work with ebpf or ptrace.

    • By snapplebobapple 2025-05-291:10

      Its not that practical. I cr#ated a nix file to roll a new jellyfinmediaplayer client in nix and it worked until s(me update broke it but the audio and a few other things needed finding obscure nonsense and specifying it to work passably. Its been replaced by fi e sentences remnding me what to click installing bazzite and it works rock solid for very mnimal additiobal setup time

  • By urlwolf 2025-05-2616:193 reply

    I moved to nix around Nov last year and couldn't be happier, the motto 'nix fixes that' is true. First time I can say linux is trouble free. Upgrades are painless. Dev environments, reproducible. Largest repository of packages in the linux world. Next to zero time wasted configuring things. Foundation LLMs now know enough nix to get you out of trouble most of the time. It's perfect as a linux experience.

    • By TeeMassive 2025-05-2616:222 reply

      Until your use case hasn't been deciphered by a Nix scribe and then you have to fight the magic.

      • By johnisgood 2025-05-2616:223 reply

        Which makes "next to zero time wasted configuring things" false, very much so. You will have to write your own derivation(s) using Nix[1].

        In any case, from the article, what does not apply to Guix, too? I am leaning towards Guix because of its language (Scheme, i.e. Lisp-y), but I wonder about the differences between the two, today (besides userbase and hype).

        [1] https://nix.dev/tutorials/nix-language.html

        • By ayrtondesozzla 2025-05-2616:562 reply

          Agreed on Guix. Used it for a few years, absolutely loved it, excited to go back (had to move off for unimportant and unrelated reasons).

          Would love to hear from someone who has used both though.

          I've never seen an excellent, detailed comparison actually, as conversation on the subject tends to devolve into a "discussion" on ethics. Meaning, people who dislike GNU or GPL or Lisps or something get testy and argue uncharitably (imho, please prove me wrong, not flaming here, etc).

          This is ironic, to say the least, as one of the main points of the proponents of the "anti-GNU" side tends to be how Guix is too opinionated and pushy and hard-line etc. So we've a classic upside-down situation, which is a real shame, as Guix seems to be in reality a practical project with lovely people involved that's doing very interesting work.

          • By pxc 2025-05-2617:43

            The Guix blog has really good and detailed discussions of things Guix has done differently than Nix! Posts on the choice of different abstractions for modeling services in Guix is probably a good starting point.

            The UI differences are also striking right away. Guix has a more unified CLI, and the main Guix repo seems to more quickly absorb functionality that's split into various community projects in Nix.

          • By bfrog 2025-05-2718:441 reply

            Guix not supporting non-free packages out of the box is the only real issue I have and that’s directly tied to the gnu origins. If guix were as pragmatic as NixOS and nixpkgs then I don’t think I’d have anything to say, lisp is way nicer.

        • By mplanchard 2025-05-2616:442 reply

          Mainly the mindshare. Nix has a massive set of packages. You’ll have to build more derivations for guix.

          • By pxc 2025-05-2617:461 reply

            Guix already has as many packages as Nixpkgs did back when I started using it, and like Nixpkgs, the package set is growing exponentially. What you say is true but it also seems to me like for many, Guix already provides a very useful starting point.

            • By mplanchard 2025-05-2714:41

              I hope you’re right. I love nix and NixOS, but I really don’t enjoy the nix language. A lisp would be so much more pleasant.

          • By johnisgood 2025-05-2616:511 reply

            I assumed that, but "only" that? I genuinely wonder. I did not get around to try Nix yet.

            • By mplanchard 2025-05-270:04

              I think it’s “only” that yeah. Both have fundamentally the same capabilities. Nixpgs may provide more out of the box library functionality than guix, I’m not sure, but as far as I know there’s nothing possible in one that isn’t possible in the other. Would be glad to be corrected by anyone with more experience, though. I’ve run NixOS for years now, but have only dabbled with guix.

        • By xedrac 2025-05-293:35

          Guix solves the biggest problem I have with Nix. The awful Nix language. Scheme is so much nicer it's ridiculous.

      • By bigyabai 2025-05-2616:251 reply

        I guess relentlessly dylibbed software like AppImage is the elephant in the room. Nix struggles to handle those types of programs, but they tend to work fine in Flatpak or with a special tool (eg. appimage-run or steam-run).

        • By sshine 2025-05-2619:05

          Struggle how?

          There’s an entirely mechanical workflow for calling patchelf when packaging a binary that was compiled dynamically against library paths outside of the Nix store.

    • By sepositus 2025-05-2616:494 reply

      Welcome to the honeymoon phase. Mine lasted about a year. Eventually you will have to leave the comfortable area of "those who have done it before" and engage with some long, unwieldy, mostly undecipherable stack trace.

      When I asked a long-time Nix vet why he thinks people leave, he provided the most insightful answer I've seen yet: they just don't try hard enough.

      • By nextos 2025-05-2617:112 reply

        I think Nix is getting an unfair reputation for being too hard. Simple things are IMHO simpler in Nix than in any other distribution, and what one needs to know to accomplish them is tiny. It has basically reduced my sysadmin maintenance tasks to zero. In case of regressions, Nix makes it trivially easy to go back in time 2 or 3 years, cherrypick some packages and install them, or change your entire desktop environment, and then go back to the previous state with no effort.

        Nix is hard if you need to build something difficult to package or something that has very unusual or dirty build processes. If you need to that regularly, Nix is not worth the effort unless you are an organization that values reproducibility and is prepared to pay for its cost upfront. If these particularly messy usecases are something that you don't encounter so frequently, you can always use an escape hatch to be able to make your install impure. For instance, distrobox is quite convenient to run e.g. an Arch or Ubuntu container. In my case, this has been helpful to run Julia, whose Nix packages are quite brittle.

        • By davidcox143 2025-05-2619:242 reply

          I've been using Nix and NixOS since 2022. I can't imagine not using Nix at this point and agree that the reputation for "being too hard" is not quite accurate. Nix is different - that's the point.

          The learning curve is a thing, although I'd argue that it's nowhere near as steep as the tools many of us use every day (C++, Rust, AWS/GCP, etc.)

          Nix's "difficulty" IMO comes from defaults that are not sane and a split community. For example, if you use the official Nix installer, flakes are not enabled by default (despite being widely used [1]), but they are if you use the Determinate Systems Nix installer.

          Flakes are realistically the only way to obtain the benefits that motivate learning Nix (deterministic pure builds, fine-grained control over dependencies) and are the "primary driver of Nix's adoption" [2]. AFIK there isn't a viable alternative to flakes other than maybe atoms [3], which are relatively new (like "lock files are totally hand made" new [4]). Yet, the official Nix stance on flakes is to wait... for... what?

          For a day-in-the-life look at more of Nix's rough edges, I posted some rambles here [5].

          [1] https://x.com/d4r5c2/status/1896415101386928539 [2] https://x.com/grhmc/status/1896551138104844389 [3] https://x.com/nrdexp/status/1925892763301695978 [4] https://x.com/nrdexp/status/1925707692447871283 [5] https://youtu.be/TwVamLq5OHY

          • By codethief 2025-05-2622:081 reply

            Do you have a link that explains what atoms are? This is the first time I'm hearing about them.

            • By davidcox143 2025-05-273:561 reply

              Sort of. There’s a summary by the author of atoms in the Twitter thread linked above.

              They also link this very lengthy blog post:

              https://nrd.sh/blog/atom-anatomy/

              My understanding of atoms compared to flakes is that they 1) add toml 2) provide a more precise way to reference remote src.

              The author also claims performance benefits. I haven’t used them personally and can’t speak to their stability or ergonomics.

          • By nylonstrung 2025-05-2715:551 reply

            I like your Atoms idea and will follow along

            IMO 80% of Nix's shortcomings are due to 20 years worth of tech debt that we're all very conscious of

            By 2030 all the rust rewrites and new tooling will have finally saved us from it

            • By bfrog 2025-05-2718:411 reply

              Unless it becomes a typed language with clearer syntax around what is what, it’s painful at the scale of nixpkgs and nix without nixpkgs just isn’t all that useful.

        • By jrockway 2025-05-2621:261 reply

          I think the reputation is fair. Why is flakes still experimental, for example? That's a subtle bit of encouragement to do things the old way and as a result, documentation is always mixed and you end up "but how do I do that in a flake".

          Something that is in theory nice is using the same packages in development and production. But "everyone" uses Mac OS for development and Linux for production, and if you want to guarantee that every developer on your team isn't recompiling Node from scratch, you want to use nixpkgs-25.05-darwin instead of nixos-25.05 on Mac OS. The result is that you aren't actually getting the same package across systems, which is rarely problematic but is something that Will Go Wrong someday. Why not keep Darwin stable in the main stable branch?

          I have also found the entire system incredibly unstable during release pushes. Lots of stuff broke in 24.11 and unstable as 25.05 was being prepared (notably nodejs_20). What I learned from this experience is "don't touch package updates in May or November" which isn't amazing if you care about security updates.

          So basically, Nix is incredibly rough around the edges. nixpkgs is a much better package repository than anything else I've used, but it's not perfect. It's generally more up to date than Debian unstable. It supports more platforms than Homebrew (which doesn't work on linux-aarch64, a platform I use heavily). Overall the philosophy of making each package entirely self-contained is the right approach.

          NixOS is also fine, mostly... but I use Bazel to build all my personal projects. Bazel does not work well on NixOS. For some reason, nixpkgs doesn't have Bazel 8 which is the version I use (because if you don't update your project to a recent Bazel today, you'll have to do it tomorrow). If you get a NixOS-compatible bazel 8 from some random flake, you can solve that problem. But then there are a lot of assumptions the Bazel ecosystem makes, and those are unresolveable. To the Nix folks, having your build system download the official distribution of Go, verifying the sha256, and execing it to do your builds is unthinkable. Personally, I'm fine with it. The Go team knows how to release Go better than anybody. But this impedance mismatch makes it nearly impossible to have a project that builds on "normal" Linux and NixOS. You can go full Nix and get go, c++, etc. from nixpkgs, but then everyone has to have Nix before the can work on your project. I would be OK making that decision for myself (I already use Nix), but I imagine it's a hard sell if you just want to fix development at work. People would complain. People will run into problems. To some extent, this is Bazel's fault (and the ecosystem) for assuming that /bin/bash and some vaguely recent /lib64/ld-linux-x86-64.so.2 exists. NixOS says "no it doesn't unless you declare it and get it out of $PATH" but honestly which version of bash runs "exec bazel-out/program-that-was-just-built" is irrelevant in practice, so it's just an unnecessary barrier. There is an attempt at compatibility for those that don't care about versioning the version of Bash that each shell script runs (envfs, nix-ld), but at least for me, it doesn't work. All in all, the net result is that I can't actually do work on NixOS, and I can't write a flake so my home-manager configuration can install all the software I've written for myself, which is a pretty bad feeling. Building my projects is easy... "git clone git@github.com:jrockway/monorepo; cd monorepo; bazel build //jlog/cmd/jlog; cp bazel-bin/jlog/cmd/jlog/jlog_/jlog ~/bin". But it's literally impossible on NixOS simply because something in the 10,000 lines of other people's code wrote "#!/bin/bash" somewhere. That's pretty rough.

          My TLDR is if you want the latest version of Git on Mac OS, linux-aarch64, and linux-x86_64, you should probably look at nixpkgs and home-manager. I like 'em. I don't think there's anything better. Everything else... it's rough. When you commit to leaning into Nix, a lot of your free time is going to disappear for a while.

      • By atrus 2025-05-2617:041 reply

        Why is it inevitable that you have to leave the comfortable area of "those who have done it before" though?

        Most of the benefit of nix for me is maintaining a configuration for a couple computers in a way that's easy to backup, upgrade, and recover with.

        • By sepositus 2025-05-2617:34

          For reference, I use Nix to manage three different machines, always via home-manager and nix-darwin (I left NixOS awhile ago and haven't looked back). I don't think it's "inevitable" that you'll hit the difficulty wall, but certainly likely.

          As an example, I was recently playing with Pyinfra which is like a pure Python version of Ansible. It turns out that one of the dependencies uses an archaic version of setuptools and the package owner had inserted some _very_ hacky code that ended up breaking on two of my systems. Now I'm relatively experienced with Nix, so it took me a few hours to track down, but it would have been days if not impossible for a beginner.

          Nowadays I package brew along with my machines and as soon as something smells funky in Nix I just manage it with brew. Much more peaceful.

      • By packetlost 2025-05-2618:592 reply

        Been there. Powered through it. It gets easier when you actually read the Nix manual.

        • By danieldk 2025-05-275:431 reply

          Nix Pills are probably the best way to get a deep understanding of Nix and some underpinnings of nixpkgs. I read the pills when I started using Nix in 2018 and never had much difficulty understanding the language or most of nixpkgs.

          • By packetlost 2025-05-2716:13

            The biggest thing I've hit is linking issues with Rust -sys crates (ie. C/C++ libraries wrapped). There's some very strange behavior if you include gcc and clang (for example) into the same environment.

            My biggest issue with Nix tbh is it adds an annoying step to every random repo I want to clone and build.

        • By kstenerud 2025-05-2620:58

          Been there. Powered through it. Read the manuals. Stuck to it for a year before asking why I was behaving like a masochist.

          Now I use Debian and LXC and docker.

      • By exe34 2025-05-2617:281 reply

        > Welcome to the honeymoon phase. Mine lasted about a year

        Mine has been going on since 2016, what am I doing wrong?

        • By sepositus 2025-05-2617:362 reply

          > they just don't try hard enough

          The answer was in my post. Nix isn't for everyone, and that's OK.

          • By myaccountonhn 2025-05-2618:20

            I've returned and my new approach was to learn:

            1. The repl for exploration 2. The language (read nix.dev) 3. Read the nixos manual

            And with that it started to make sense.

          • By exe34 2025-05-278:53

            I've actually gone the other way - not everything is meant to be run. If it's not in the nix repos, I'll try the fhs, and if that doesn't work - well I probably don't care enough to beg it to run. For me the fact that my base installation can never break is not negotiable.

            I definitely didn't learn much of nix(os|pkgs). I never bothered learning about flakes. I just have a configuration.nix and a user config.nix, and a "fhs" default.nix and that's it.

    • By zachlatta 2025-05-2616:202 reply

      Can you link your dotfiles? I’ve been having trouble figuring out a good way to structure mine

      • By sshine 2025-05-2617:21

        Yes, that’s easy.

        Here’s a very small example:

        https://github.com/sshine/nix/blob/main/shared/home-manager....

        My servers don’t have that many dotfiles because most server software can be configured in /etc (zsh, vim), while my work computers have a lot of dotfiles symlinked into ~/.config/ via Nix, e.g. VSCodium, ghostty, …

        Most people prefer to Nix up their dotfiles, which provides some advantages (e.g. universal styling via Stylix), but the main drawback that I’m not buying is: I can’t share my app-specific config with non-Nix users.

        But if you’re looking for a cheaper (in terms of complexity) dotfiles manager: https://github.com/yarlson/lnk

      • By i915 2025-05-2616:52

  • By OscarCunningham 2025-05-2617:482 reply

    I recently installed NixOS on my laptop after years of using Debian, and the main difference I noticed was that it's much easier to keep everything clean.

    For example with Debian I might do something like try to get my GPU to work by installing nouveau, try various configs, then uninstall nouveau and install the Nvidia proprietary drivers. But then when I finally got the GPU working, I'd worry. Are some of those config changes I tried for nouveau still affecting my system? Has all that mucking around left my GPU forever in a suboptimal state?

    With Nix, it might take just as much tinkering to get the GPU to work. But once I'm done, I know that my system is exactly as I've defined it in configuration.nix. Anything else I tried along the way will just sit inertly in /nix/store until the garbage collector wipes it away.

    • By layer8 2025-05-2620:162 reply

      I don’t know. My concept for reproducibility with Debian is a backup of /etc and the list of installed packages (the output of `dpkg --get-selections`). It’s also not difficult to diff one’s /etc with a pristine version, though I haven’t actually needed that in many years.

      • By __MatrixMan__ 2025-05-2621:421 reply

        If that scratches the itch, great. Backups of /etc can't be combined in a way that lets you reliably uncombine them later though, so you're going to be limited in how modular you can make it.

        • By layer8 2025-05-2621:46

          That’s true, though using version control with branches and merges can get you pretty far.

      • By OscarCunningham 2025-05-2621:051 reply

        I believe 'dpkg --get-selections' lists all packages, not just the ones the user installed. So if I installed some package foo that had a dependency bar, this method would have me carrying around bar even if foo updated to stop depending on it.

        • By layer8 2025-05-2621:27

          You are right. In that case, apt-clone is better, though not universally available.

    • By pabs3 2025-05-2711:17

      The Debian cruft-ng package, plus package list diffs, can identify problematic state you have added to the system.

HackerNews