Quick summary for those struggling with time management:
“What they solve” - nada[1][2][3][4][5][6],
“Why they matter” - they don’t[7],
“The future” - slow deprecation[8].
EDIT: Add refs
Dropping upstream Nix from Determinate Nix Installer - #51 by roberth ↩︎
It's not about “Flakes vs. Channels” — Samuel Dionne-Riel (samueldr) ↩︎
I'm not convinced flake inputs should exist - #16 by SergeK ↩︎
A call for the Nix team to present a unified front on the outcome and strategy around Nix flakes - #124 by SergeK ↩︎
Flakes aren't real and cannot hurt you: a guide to using Nix flakes the non-flake way - jade's www site ↩︎
What are you even talking about?
No guarantee of reproducibility in your dependencies.
How is this true for someone using some other pinning tool?
For example if you builtins.fetchTarball { url = "..."; sha256 = "..."; } a project that, itself, uses e.g. import (builtins.fetchTarball "https://.../unpinned-archive.tar.gz" {}) somewhere, your inputs are pinned, but their inputs aren’t.
Due to the misdesign of how Flakes were brought into Nix, there is no generalized mechanism to forbid patterns that would produce “unpinned” results.
I’ve read the blog post and, at the risk of being shouted at for pointing out things, I am questioning some of it, and have questions about it.
The main problem I can see is the consistent use of the word reproducible (and reproducibility). The commonly shared definition of it would be the one from the Reproducible Builds project, which states
A build is reproducible if given the same source code, build environment and build instructions, any party can recreate bit-by-bit identical copies of all specified artifacts.
While that is not universally agreed upon to be “the” definition, I think it’s fair to claim it de facto means bit-for-bit reproducibility of the outputs. Especially in the context of build tooling and package management.
In this article, only one of the eight use of reproducible (and reproducibility) mean that. Well, another one could mean that, the one that links to the zero-to-nix page on the topic, which brings me to the next concern.
Every flake also comes with a
flake.lockfile that records input revisions. That means that the entire dependency graph becomes deterministic. […]
This makes Nix reproducible in a way that you can’t opt out of even if you want to.
The linked page’s lede states:
[Reproducibility] The ability to consistently and repeatedly produce the same build outputs from the same build inputs.
The linked reference makes the same conflation between a repeatable build environment, and a reproducible build. So I’m not too surprised to see that in this article.
So I don’t think there’s any benefit of the doubt to be had here: either this is meant to be misleading people into believing Flakes are making Reproducible Builds (capitalized noun), or the authors don’t internalize the distinction…
Though given the article (but not the linked reference) states that “builds […] can’t be guaranteed reproducible”, I’m not sure both options are equally likely. Especially with the way the article introduces the problem that there is “no guarantee of reproducibility in […] dependencies”.
While that likely really means something like no guarantee of hermetic/deterministic inputs, the wording inevitably sounds like bit for bit reproducible inputs.
Why am I bringing this up? Because I believe it is crucial that major actors in the NixOS ecosystem consistently and diligently stay transparent with the claims of what Nix can do.
Nix is one level removed from the problem reproducible bits-for-bits builds. Nix instead is what enables a consistent and repeatable build environment that enables bit-for-bit reproducible builds to be achieved.
Getting to these environments (if I’m allowed to simplify) is achieved by making build sandboxes, for every build, and those are made from the instantiated derivations (.drv). The .drv are the mechanism that, when the environment and project would be, produces a bit-for-bit reproducible build.
Any method to get to a given instantiated .drv gives you the same repeatability claims that Nix does. What “Flakes” does, though, is use some special-cased codepaths in Nix to forbid evaluation-time inputs from being left “unpinned”. If there had been a construct in the language or tooling to ensure any “reference” was “pinned”, the same guarantees would apply. Though general improvements on some areas of Nix have been blocked in a stalemate for a while…
Nix builds are not inherently reproducible. Though Nix build environments can be. The Nix Expression Language is how you traditionally compute those environments. The Nix Expression Language and its evaluation can use “reproducible builds” concepts to produce a repeatable build environment i.e. producing the same .drv. And just as with Reproducible Builds, this can be achieved when the same inputs are either specified or accounted for.
Now, for an actual more precise question, rather than questioning:
I’m wondering what was meant with this passage:
On top of this, Nix workflows were tied to file naming conventions—the
nix-buildcommand tied todefault.nix, thenix-shellcommand tied toshell.nix, and so on—with poor discoverability compared to a lot of modern developer tooling.
This is comparing this to “a lot of modern developer tooling”, but I don’t know what is meant here. Which, “modern developer tooling” or which pattern in those is this about?
Many tooling I know of, modern and older, use different entrypoint files to do different actions. And some use the same entrypoint file to do different actions. Neither are inherently more or less discoverable.
Though having one file per entrypoint to me feels like it is more discoverable, as ls shows me that the project can use nix-shell… while seeing a flake.nix gives me no information about its capabilities.
Can you expand on what this means, and how Flakes solve that? I don’t think the article addresses this, though to be fair I could have missed that.
That’s how you compete with Pixi!
Glancing over the “entirely unusable in big repos unless you use a proprietary fork” detail of course.
The lockfile is great, the new CLI is great, the output schema arguably helps with discoverability but being FORCED to evaluate from store isn’t great at all.
I don’t know anyone who keeps evaluating the same expressions over and over to benefit from evaluation caching, if I want to eval from store I can still do so in normal Nix.
The best feature of flakes in my opinion is builtins.fetchTree, the rest is nothing for me.
@lucperkins I have to agree with @SergeK, I don’t see how they solve anything or are the future. The popularity probably comes from the lockfile and better UX than their technical merits. It’s a shame that NixOS still defaults to channels and /etc/nixos and if you want your own expressions that you have to use --file and --attr to build your system, it’s almost like non-flakes are intentionally kept back to promote flakes.
This is super-unhelpful. They provide a sensible and relatively simple way to pin inputs, and lots and lots of people uise them.
I am 1000% certain people who use flakes would keep using flakes if they didn’t force evaluation from store.
Agree that downloading an entire repo into the store is suboptimal if the repo contains lots of other stuff. That said, for folks who don’t use monorepos, it works pretty well. I’m not keen on redownloading the entire contents of a large repo (nixpkgs even) on any tiny change, but it also seems on-brand for Nix, which isn’t shy about using bandwidth and cycles to provide repeatability. Can you describe why in this particular single case it’s totally unsatifsactory? I may be totally missing it.
It’s not just about downloading big repositories, even the repo you are working on is copied into the store on any little change. Any mildly sized project will end up with tens of gigabytes of store contents in absolutely no time, you don’t need a monorepo to cause this.
If it were just the inputs that’d be completely fine - as you say, this is very on-brand for nix and in fact the only way to assert input stability. But it’s genuinely awful when it applies to code you’re actively working on, and is expected to change every 5 minutes or so.
Lazy trees help, but they don’t solve the problem generically, there are many types of project that are not helped by them at all; IME mostly those projects that actually need to rebuild themselves a lot.
Flakes have completely immobilised the project for years because of their ill-conceived rushed nature. It is hard to innovate when there is an official solution, no matter how bad it is. Every month or so the office of flakes propaganda shows up to make sure that no one forgets that flakes solve all your problems, and you absolutely shouldn’t look elsewhere. Not using flakes is legacy. I think you’ve had very trenchant insight into some of the activism going on in nix. This is just another aspect of it, from some other faction.
Also if I had a dollar for every time someone didn’t understand why a file was claimed to be missing (because it isn’t tracked in git) or why valid nix isn’t a valid flake (because flake.nix isn’t confusing at all) or any of the other just genuinely nonsensical couplings to pure-eval I’d have at least maybe enough money to power my own AI-powered anti-flake slop machine.
Agree. Maybe I’ve just come to accept that Nix is a terrible platform for developing software, but a very good one for distributing it. I tend to use things like devenv to create the software, and when it reaches some natural release point I create or modify the Nix packaging for it. I’m sure this just can’t be done for low-level stuff like out of tree file systems and the kernel and whatnot, and I can see it being terribly irritating to have to develop that stuff with a tool like flakes. Maybe I should be more demanding of Nix as a development tool, but it does such a wonderful job of being a distribution tool. I give it a pass. Flakes are pretty great in distribution mode, for me, at least.
Devenv uses flakes under the hood, and it’s pretty slow as a result (along with doing way too much evaluation), so maybe I give them a pass even there, and I shouldn’t. I dunno.
I think you may have answered me a few years ago when I asked the same questions. Agree that it has been super annoying.
It is hard to innovate when there is an official solution, no matter how bad it is.
The official solution for deployment is NixOS/nixops. Noone uses it. The official solution for hosting a binary cache is nix-serve. And yet there are dozens of local and SaaS solutions for sharing a cache. This statement is categorically false.
Every month or so the office of flakes propaganda shows up to make sure that no one forgets that flakes solve all your problems, and you absolutely shouldn’t look elsewhere
Who are you arguing with? Nobody is claming that.
It is of course easier in a layered architecture to innovate further up the stack. This is the network equivalent argument of saying look at all these competing chat protocols, of course tcp is easy to change.
There is snix and lix, and both of them are multi-year efforts just to get bug-for-bug where nix is, because nix isn’t really moving anywhere because it’s been saddled with trying to unpick flakes. I’m not engaging further because honestly enough ink has been spilled here and its boring. Disagree with me, I don’t care.
Neither of those examples are as integrated as flakes are, and are trivially replaceable.
Weird argument when detsys has convinced the majority of new nix users that they need flakes, and said users enter the ecosystem not knowing anything about nix and just copy-pasting flake-related code all over their repos, precluding any other nix-ish way of interacting with the code, because flake.nix is not nix. flake-compat is a bandaid to pretend that flakeless nix could be served by said flakes, but it only exposes a fraction of the flake outputs.
If you want to see what a project that actually tries to serve both flakes and non-flakes usecases looks like, see hjem/default.nix at 4af40a15e30e29b63bd861298cb7e85d6e1ef98b · feel-co/hjem · GitHub and hjem/flake.nix at 4af40a15e30e29b63bd861298cb7e85d6e1ef98b · feel-co/hjem · GitHub. I did have to spend a fair bit of time trying out my own patterns, and this may not be the final result - but I think it works for now.
I’m disappointed but unsurprised that there’s really no pattern like this being promoted by the flakes proselytizers, because there’s no incentive for them to actually support flakeless usecases. It’s a bit extreme to say that flakes prevents such innovations, but I think flakes take up too much space to let these patterns flourish. I’ve even seen some folks that have been here for years not even aware that the nix3 commands support paths without using flakes.
OTOH I have never heard of nix-serve and nixops has been dead for over half a decade. No one is using those. And more importantly, they are not part of nix (and flakes didn’t have to be either).