Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

Yes.

It's not the functional nature of Nix that is hard to deal with. Nix's functional nature is actually its saving grace.

My difficulty with Nix is that there are important subjects that aren't taught.

I figured out how to create a usable `default.nix` and `shell.nix`, but that takes a lot of effort, and there really aren't many resources to reference.

Nix expressions use a lot of predefined functions, and I have no idea where their documentation exists, if it does at all.

Even if there is some explanation for all of the functions that I need to use, there isn't documentation for how to fit them together.

Instead of being in dependency hell on the outset (like most package managers), I find myself in dependency hell when I want to do something different. The Nix community has an irrational hatred of version numbers, meaning you have to scour the web for a derivation that gets the right version of a dependency, and "pin" it, or hope that the channel you are using has the correct version. Even when channels do have the version you want, sometimes that channel provides multiple package versions, some with the version in the name.

Names are a mess. Since every package must exist in the same namespace, but its derivation is helpfully sorted into a directory tree, the same packages will often have different names. There isn't a good way to search or browse for them either. I end up digging through github quite often, which is a lot more often than the never that I should find myself doing so.

Nix is wonderful. NixOS is by far my favorite OS, and that is why I deal with all of the rough edges, but they certainly are rough.



sort by: page size:

I've tried to use Nix on and off for years. I always run into issues where I need to write some package and

(1) I get mired in packaging low level transitive dependencies and things which seem like they should be easy end up being nearly impossible

(2) Basic things like figuring out the argument types for a particular nixpkgs function take an insanely long time. It always involves grepping around the whole repo (nixpkgs is huge for those who don't know) for invocations to find the variable that gets passed into that function and then grepping around the repo for the function that produces that variable and so on until you get to the source type. Things have awful names and documentation is sparse.

I'm a big believer in Nix as an abstract concept, but the execution has been a miserable experience for me to the extent that I can't make sense of people who report such positive experiences. I would think I'm doing something wrong, but so many people have corroborated my experiences and when I bring specific issues to experienced Nix users they also get stuck.

Ultimately, I've reluctantly gone back to dealing with Docker and system package managers because their failure modes are rarer, more predictable, and more easily worked around.


Nix is... tough. I really want to like it, but every time I pick it up I end up having to write some custom package definition for some obscure transitive C dependency with its own snowflake build system. Couple that with poor documentation for existing packages, the terrible search engine experience ("Nix" and "Nix packages" usually turn up things about Unix or "Congress Nixes Aid Package"), and a thousand other papercuts and it just seems to create more problems than it solves. I dearly hope this changes.

I'm a nix fan and have been using nixos for years now, packaging stuff and writing my own nix-based libraries, but I still find the language to be painful.

The problem is, documentation is sparse to non-existant, error messages are poor and often far from the actual problem, and it makes use of concept that lots of people are not familiar with (like lazy evaluation) without ever explaining them, resulting in surprising semantics.

But as far as I can tell, the nix devs are fully aware of these issues - they are affected by it too! The problem is, solving each of those issues is hard, and requires a lot of focused efforts.


I've had a lot of problems using Nix in the past. Not least of which is that it's terribly hard to search for anything because of the name--not only is the name generic ("nix package manager" -> "Congress nixes aid package...", "nix" -> "*nix", etc), but search engines have a terribly hard time figuring out what nix project I'm inquiring about ("nix package manager", "nix expression language", "NixOS", specific Nix derivations, etc).

Another problem is that the documentation for individual functions is nonexistant. If you want to know how a particular function works, you have to go to the nixpkgs repo in github and try to guess about where the function's definition would live in the massive directory structure, and once you find the definition you care about, you have to read the source code (in Nix expression language--if you're not well acquainted, good luck: the docs are pretty terse and hard to Google for as previously mentioned) which likely references other functions, and since imports don't seem to correspond with the directory structure, you're back to guessing where the symbol you're looking for actually lives. The problem is exacerbated by the dynamically-typed nature of the expression language; other dynamically typed languages at least tell you what type of arguments they expect, but since these definitions aren't annotated at all, you're left guessing. Documentation would do wonders for the project, especially if it's autogenerated like other languages and put in an HTML format so we aren't left hunting around the nixpkgs repo.

What Nix strives to be seems promising, but it's really hard to even evaluate the core ideas for all of the usability issues.


Yes and no. Fundamentally, I agree with you. Nix is hard because it solves an inherently complex problem. But it's also hard because it has some usability flaws.

For example, I recently wrote a bunch of Nix and got an error that boiled to me forgetting to add a "name" to a derivation. That is absolutely my bad. However, Nix didn't tell me which file this derivation was in (it said 'unknown file' iirc), the stack trace also wasn't any help . And so I went hunting through the hundreds of lines of Nix I just added to find where I might be missing a "name".

Should I have tested my code incrementely? Absolutely, yes! But Nix should also be able to tell me which file I am missing the attribute in.

I say all of this as a huge Nix advocate and fan. It's wonderful, it solves so many problems I previously had and I intend to keep using it, but it's far from perfect.


I think the reason why Nix is hard isn't the language (although lazily evaluated functional language is definitively a part of it), but the paradigm shift, but this is necessary for reproducibility.

Imagine if you had a distro where you couldn't depend on existing state (so you couldn't just compile something to /usr/local). Where you had to create package definition with all dependencies explicitly defined.

Kind of like using FreeBSD ports (or Gentoo) without precompiled packages and you were forced to add every package manually before using. You would also complain it is hard even if you would have to just use Makefile and shell script.

I don't think this will get easier until Nix would get embraced by other package s, making it easy to specify those components as dependencies. Now with flakes with can be composable this is possible.


It sounds like your issues with Nix stem from its steep adoption curve, rather than any technical concern. This _is_ a concern for a team that needs to manage it - I agree.

I'm quite diehard in terms of personal Nix/NixOS use, but I hesitate to recommend to colleagues as a solution because the learning curve would likely reduce productivity for quite some time.

That said - I do think that deterministic, declarative package/dependency management is the proper future, especially when it comes to runtime environments.


This might get down voted, but here goes. I agree that the Nix documentation can be confusing and even contradictory at times. I agree that there is a steep learning curve to getting started with Nix and NixOS. However, I do not think that Nix has ever claimed or intended to be a tool that can be used easily by everyone.

If the ideas seem pointless and confusing to you, that's fine, don't use it. There are bunch of great package managers and linux distros that will probably suit you better. But, if you see the need for declarative package management, then Nix is for you.

I'm a phd student in computational science. I use a bunch of different packages on my computer and I need to know exactly what I've installed and I do not have time to debug problems from dependency hell. Nix and NixOS have been two of the most interesting and useful tools I have every used. Not only have a gained huge productivity boost from having reproducible environments and declarative package management, but I've thoroughly enjoyed the process of learning these unique tools that, for me, are leagues better than any package management system I've used in the past.

If it didn't work for you, I hope you find a tool that suits you better. But that doesn't mean its bad. It might just mean it wasn't intended for you.


Very much agreed; nix is its own world, which makes it really hard to get into. I wish someone had written "nix but in python" (or bash, or whatever) that still used a nix-store equivalent, kept the overall design and the immutable and reproducible packaging, still connected everything by hash, did all the hydra-style build infrastructure.. but didn't use nix-the-language, and made an explicit effort to use more conventional language wherever reasonably possible. Ideally, it'd even let you write expressions in arbitrary languages; it should be perfectly possible to say, "here is a directory containing 'inputs' and 'output' subdirectories based on your declared dependencies; place any build steps you want in build.sh, so long as they deterministically populate 'output' from the package directories in 'inputs'" (and then run the build a few times, without network access, to make sure). I don't see anything in nix that actually needs nix-the-language, or even a functional language at all, and I think nix-the-package-manager would be far more accessible if they'd not tied nix-the-language to everything.

I can see the utility of using Nix for managing dependencies of software you're developing, because that can be horrific to deal with and Nix makes it fairly elegant to do, depending on what ecosystems it's being used with.

Using NixOS to manage your whole system, on the other hand, I don't think I'll ever get behind. Maintaining reproducible builds of my system, while being nice to have, doesn't really solve any problem that I actually have. On the other hand, it introduced a myriad of problems that plagued me on a regular basis before I switched away from it.

The first problem I had was that it added steps to a number of tasks I had on a regular basis. For example, updating or adding new extensions to VS code. Because NixOS is immutable, you don't get to benefit from just running a single update or install command within VS code like you would on a normal system. It becomes an annoying series of steps you have to carry out for each operation, all in the name of being convenient to hypothetically down the line. Multiply this by all the tools you use and it gets tiresome.

The other issue is that you raise the barrier of entry for every new piece of technology you want to experiment with. Now, in addition to learning how to use any tooling involved with the ecosystem, you have to figure out how to get it to play nicely with Nix. This works very well for some ecosystems, like Haskell, and not well at all for others. I was just getting started with Android development at the time, and just when I thought I had it working nicely with Nix, I would run into a new problem that would need investigation and band-aids.

I wish NixOS was more convenient to use than it was in my experience, because it's nice when it works well. But in my case it created more problems than it solved.


Perhaps in contrast to most people's experiences, I haven't found Nix hard to learn. I think it really helps if you have learned Haskell before. Nix (the language) is really a simple functional programming language, but Nix relies quite heavily on functional programming concepts like laziness, fixed points, etc. A lot of things are underdocumented, but if you understand the Nix language well, it isn't hard to look up definitions in nixpkgs. I can understand that it is all very alien and overwhelming if you do not have a grounding in functional programming.

The primary issue for me has been that Nix is a very deep rabbit hole. You can spend enormous amounts of time on making your configuration more functional and declarative. Pretty much like you can spend enormous amounts of time on customizing an Emacs configuration. It's hard to strike a balance. And outside declaratively defined infrastructure (servers), it's probably not really worth it. I could almost fully reproduce my NixOS system (there is always some mutable state left) with a single command. But takes many months of effort to get to that point. On the other hand, I can set up a fresh macOS or Fedora Silverblue systems with all my customizations in 1 or 2 hours and have to do that maybe once of twice a year? So, ¯\_(?)_/¯.

I think the balance is different when you manage a lot of servers and most servers can be defined as a function with a small number of varying parameters.

The other part of the rabbit hole is that software breaks frequently in Nix. Upstreams do not really develop things under the assumption of a non-FHS, immutable system. So, you were going to work on something, but before you know it is 30 minutes later because you ended up fixing some package you need and that broke. Similarly, you'll end up packaging a lot of stuff and spending quite some time making it fit the Nix mold (looking at you Python packages that mutate in-place, sigh).

I love Nix as a principle -- it's declarative, immutable, pure, reproducible. But in practice, you can reap many of the same benefits from impure, inferior alternatives, with far less work. Yes, Docker is an ugly duck compared to Nix, but it brings 90% of the reproducibility benefits and probably everyone on your team can be up and running in hours. Rust's cargo doesn't allow you to specify every non-Rust dependency exactly, but with a Cargo.lock file you can make most of your build reproducible. NixOS is a clean, immutable, declarative system, but other systems offer a subset of its features, such as atomic upgrades/rollbacks, immutable root, and isolated applications (e.g. Fedora SilverBlue + Flatpaks, Fedora IoT, macOS). These alternatives are far more familiar and easier to work with. You can get most (but not all) of the benefits of NixOS with far less work.

Worse is better.


Nix is great in theory, but the user experience is unacceptably bad, especially for anyone who isn’t a software engineer.

While it does do an excellent job of reproducibility if the inputs are the same, I’ve found it to be prone to be breaking if you switch to newer versions.

Part of the reason that it’s painful to use is because while it’s marketed as “declarative”, in actuality it’s functional, which results in a lot of convoluted syntax to modify parameters for a package, which varies based on the language of the package.

There seems to be some awareness of the usability issues, but the changes have seemed both a step forward and backwards. For instance, you used to be able to use the “nix search” command to look up package names; now it’s gated behind some arcane syntax because it’s “experimental” and something to do with flakes. And flakes seems like it has the consequence of fragmenting the package repositories and making it impractical to improve the language.

I still have helper functions to wrap desktop applications that I had to set aside, because some upstream changes broke it and neither I nor anyone on the nix forums could figure out if there was even a way to include them in the “darwin” namespace with an overlay. My goal was to make it as easy as homebrew to add an app to nix’s repository.

Another evening I sat down to make a minor feature to a Python library and decided to use a nix environment. In theory, this should have been better than a virtualenv. In practice, there’s no in-tree support for specifying specific versions of Python libraries, and mach-nix had trouble with the dependencies, so I wound up just filing a bug report and gave up on the feature I was trying to implement.

On the plus side, NixOS finally has a graphic installer, but I don’t think that helps macOS.

I’m still hopeful that the community will decide to prioritize usability, but after spending an aggregate of months of time trying to get things to work and continually running into time-consuming roadblocks, it’s not something I would recommend lightly to someone who wants to be more productive.


It's been a while since I used NixOS, but I never really had an issue with the language. What was frustrating was in package solutions there were all these special functions which as far as I can tell were undocumented and I couldn't figure out what they could do. So there was sort of this gap in terms of documentation between theoretical understanding and tooling/functional understanding which I struggled to cross.

The code in nixpkgs does contain some code that uses relatively sophisticated programming language constructs, such as lazy evaluation, partial function application, or fixpoints. -- So I'll say there's at least some friction to Nix from the language being pure/functional.

Mostly I think Nix would be just as hard if packages were expressed in any language.

But, stuff like "this program compiled by nix is using a different glibc than this library" is going to be confusing regardless of whether Nix expressions can be written with JavaScript; or stuff like some program assumes it can write to $HOME in its build script not playing nicely with Nix.

A lot of why Nix is hard is because it's weird, and that even as an end user when things go wrong it may require understanding more about what's going on than what other solutions require.


As someone who is using NixOS for 8+ years, I do agree with both your problems.

(1) I do find that it's very rare having to implement a low level transitive dependency from scratch (most low-level system dependencies are already there, and for dependencies of a specific programming-language ecosystem you'd usually auto-generate them from things like `yarn2nix` or `poetry2nix`). However, I agree that once you need to do that for non-trivial library it quickly becomes painful and require intimate knowledge of both the build system and Nix environment.

(2) This is my main issue. My workflow is also having a local `nixpkgs` clone, and go find the function and the parameters I can use every time I don't remember how to use it. I also think that it is sustainable. There are many exciting improvements going on with the new CLI, so hopefully we will get nicer error messages and better tooling support in near future.

> but the execution has been a miserable experience for me to the extent that I can't make sense of people who report such positive experiences.

There are pain points, but the advantages for me were indispensable. Being able to pin dependencies accurately, reproduce an exact development environment deterministically, and use the single language to define applications, configurations, and even entire system images is a great benefit that I would need to use 10+ tools with individual quirks otherwise.


Agree, nix itself is actually fairly simple. What it is hard though is functional programming, an that's the biggest road block for developers that are used to imperative programming.

Places that use functional language for their code base don't have problems picking it up. It is especially big in Haskell community, because Haskell is also lazily evaluated.

People might wonder why not use language that more people are familiar? The reason for it is that purely functional and lazily evaluated language is basically behind Nix's biggest strengths. Purely functional means for the same inputs(architecture + source code + dependencies + configuration options + etc) you should get the same output, there's no mutable state the languages always starts the evaluations from a known state.

Why lazily evaluated? That makes the Nix only evaluate things that are necessary to obtain given derivation.

I think when people talk about Nix being confusing they really mean NixPkgs. Which is like a stdlib of Nix. The thing is that Nixpkgs in a constant flux people who work on it work on different components somewhat independently. Also often they realize better ways of doing things, and often the documentation doesn't keep with it. I think that Nix needs some kind of mechanism for literate programming where documentation would live together with the code and would be updated at the same time. There are some effort for example like the NixOS options page is generated from the source, but that's just just small part of Nix and only applies to NixOS.

I also would love if nix would standardize on how to pin nixpkgs. I recently discovered Niv[1] and it makes reproducible builds so much easier. I think Nix also needs some opinionated tooling to create dev environment for each supported languages, currently people need to do it themselves, and it takes a lot of research.

I think Niv + lorri + direnv make dev experience extremely enjoyable. For example a python project set up that way. Once you enter the directory, you have the exact version of Python installed that has all the dependencies (of course with locked versions) and all extra development tooling that developers need with exact same versions. All you need is just run the application and it just works every time. If somebody broke something you can easily use git bisect to find when and why it broke.

Nix fails to document though how a new user get to that point, and desperately needs help there.

[1] https://github.com/nmattia/niv


100% this. The language is designed for its use case which is packaging and configuration (nothing more or less). It has a learning curve due to being lazy and functional but works great once you get the hang of it. But the documentation of all its functions is so annoying. You have builtins and the nixpkgs functions[1]. There is learning the language, and then learning how to use it. Then there is the entire ecosystem of custom packaging functions that have their own pros/cons [2]. The issue isn’t with the language but the difficulty with trying to make existing tooling work the Nix way. That part is where I agree with the curse of nix. But the effort is worth it because once the packaging is complete it just works (forever).

1: Best resource I’ve found is this: https://teu5us.github.io/nix-lib.html

2: The status of lang2nix: https://discourse.nixos.org/t/status-of-lang2nix-approaches/...


I've been using Nix for about a year. I don't use NixOS but home-manager and nix-darwin. Just about every single time I've tried to do something new, it has ended up being way more complicated than I thought at first.

- Installing emacs? You'll want to use the binary cache. No biggie. Except when it doesn't work, and your system update ends up building it from scratch--a two-hour process on my stalwart old desktop.

- Setting up a local environment for a Go project? No problem. Er, except some tools need to be installed globally, quite against the Nix philosophy.

- For a system or home configuration, you'll encounter packages, modules, and flakes. Packages are explained in the Nix documentation. The functions people actually use to make packages are explained in the Nixpkgs documentation. Modules are explained in the NixOS documentation. And flakes are explained in...uh...well, you'll figure it out. (You don't need flakes at this point, but they're on their way. And they won't replace packages entirely.)

- Debugging? gl,hf

- And this is all before we get into my admittedly complex system configuration using flakes, nix-darwin, and home-manager. Some things need to be set up at the system level, others at the home level. And cross-referencing the two is a pain. I just got an M1 mac. Some packages need to have their x86 versions installed, against Nix's protestations. I appreciate the guy who made a configuration framework that figured out how to do this, because I certainly couldn't.

Anyway, I love it.

I think it's the mathematician in me. The whole idea is too aesthetically pleasing for me to resist. The hope of having everything placed just so by a mere wave of my hand, too tempting. Damn the consequences.


The Nix language's main concern is handling large scale configuration. General purpose programming and scripting is completely out of scope.

The Nixpkgs repository has over 80,000 packages [1] contained in a single key-value store. The design of the Nix language not only makes this sustainable, but even compelling. For one thing, Nix package descriptions are composable and easy to manipulate programmatically. It's common practice to create new packages on the fly out of existing package definitions, and it's what the Nix language is particularly good at. Things like this add a lot of complex inter-dependencies within the Nixpkgs code base, and it still manages to be comprehensible.

The same approach isn't practical with general purpose imperative programming languages. A quick glance at Nixpkgs repository reveals that it contains over 2.4 million lines of Nix code. Imagine that much Java/Python code manipulating a single global mutable data structure. It can only result in a nightmare.

Additionally, the notion that the Nix is Haskell and is therefore incomprehensible is an often repeated meme without any backing details and needs to stop. If any language is similar to Nix, it's Jsonnet. But I haven't seen a single person criticizing Jsonnet [2] for being Haskell. If you can understand JSON and have no problem writing functions for any common language, then there really should be no problem learning the Nix language.

[1]: https://search.nixos.org/packages [2]: https://jsonnet.org

next

Legal | privacy