Hacker News new | past | comments | ask | show | jobs | submit login

Okay I'm a Nix enthusiast but you'll have to trust me when I say that I'm not criticizing them for moving away from Nix; it isn't that strong of an emotional attachment. However, I'm not really sure I understand some of these complaints and they really could use more explanation. For example:

> The biggest problem with Nix is its commit-based package versioning. Only the latest major version of each package is available, with versions tied to specific commits in the nixpkgs repo.

While Nixpkgs is an amazing resource, Nix != Nixpkgs. Nixpkgs is highly unideal for cases where you want to be able to pull arbitrary versions of toolchains, but it is not the only way to go. For example, there is amazingly good Nix tooling for pulling an arbitrary version of Rust. Other Nix-based developer tools have shown how you can do this well.

> no way of splitting up the Nix dependencies into separate layers

That doesn't make any sense. You can literally just split them into separate layers in whatever arbitrary fashion you'd like. The built-in Nixpkgs docker tooling has some support for this even.

> We also changed the codebase from Rust to Go because of the Buildkit libraries.

This part is not related to Nix, but I find it interesting anyways. Obviously most people don't transition programming languages on a whim, it's generally something you do when you're already planning on building from scratch anyways. To me it almost sounds like different people worked on Railpacks vs Nixpacks.

(I've definitely seen what happens when people not familiar with Nix wind up having to deal with unfinished Nix solutions within an organization. It is not pretty, as most people are unwilling to try to figure out Nix. I don't generally use Nix at work out of fear of causing this situation.)




I don't use Nix, however this seems dismissive:

> While Nixpkgs is an amazing resource, Nix != Nixpkgs.

If Nixpkgs is the default and alternatives require additional research and effort then for most users it _is_ Nix.

> That doesn't make any sense. You can literally just split them into separate layers in whatever arbitrary fashion you'd like. The built-in Nixpkgs docker tooling has some support for this even.

Is this obvious, simple, and default behaviour?


This isn’t “most users”, this is a large company building a product on top of Nix. I’m pretty sure most orgs using Nix at a minimum have a custom overlay.

If you identify these things as an issue, any competent engineer should find a variety of solutions with search and/or LLM assistance within an hour, since they’re not super obscure requirements.

I’m not saying Railway didn’t do this and realize that these common solutions weren’t viable for them, but it’s odd to not mention anything they tried to get around it.


To emphasize this point: dleslie's comment is valid on a blog post "we tried Nix for a while to manage our dependencies, we are just building an app and want builds to work, and we decided to move on". For an end user, it is absolutely understandable to assume "nix = nixpkgs".

But as kfajdsl points out: that's not what TFA is. This is a company building a product on top of Nix. Package management is their expertise. Anyone using Nix in that capacity understands the distinction between nix and nixpkgs. Which they certainly do--GP only remarked it was odd they didn't explain it, not that they didn't know.


Nixpkgs isn't Nix and in production you rarely just use Nixpkgs verbatim. It's trivial to overlay whatever versions you want (including forks), and I'd say it's expected for any company in production to manage their package set.

We are talking about a company full of professionals. If they need something obvious, simple, and default to manage their build - the core business function that turns their text into deployable artifacts - maybe there is a skill culture issue.

The industry is full of ineptitude though.


> The industry is full of ineptitude though.

While I disagree with the person you're replying to, I find your reply dismissive.

I don't know the behind-the-scnene reasons for this, but I can very very easily apply a very similar situation to this from my experience.

Nix is a full blown functional programming language along with a very rich (and poorly documented, niche, only second to C++ template in error comprehensibility[1]) ecosystem in itself. It's not like "docker" or "kubernetes" where you're mostly dealing with "data" files like yaml, json or Dockerfile. You're dealing with a complex programming project.

With that in mind:

- You have a core team with 1 or 2 people with Nix passion/expertise.

- Those people do most of the heavy lifting in implementation.

- They onboarding the team on to Nix

- They evangelize Nix through the org/company

- They mod and answer all the "#nix-discussions" channel questions

Initially the system is fairly successful and everything is good. over the next 5-6 years it would accumulate a lot of feature asks. The original "Nix person" has long left. Most of the original people have moved either to other projects or not particularly that passionate about Nix. In fact, the "best" developer you have who has inherited the whole Nix thing has only really had to deal with all the shit parts of Nix and the system. They are they ones fixing issues, dealing with bugs, etc. All while maintaining 3 stacks, a Nix stack, a Go stack, and a Rust stack.

Eventually that person/team that's annoyed by maintaining the Nix project wins. They want to own that code. They don't want to use Nix any more. They know what's needed, they want to implement it as part of their main Go stack that they are actively working on. They can optimize things for their special case without having to worry about "implementing it the Nix way" or "doing it upstream".

They promise you (the management who is open to the idea, but trying to understand the ROI) feature parity + top 5 feature asks for the initial release. You trust the team enough to let them do what they think is best.

[1]: LLMs are really good at suggesting a solution given an error message. Nix errors bring them to their knees. It's always "Hmmm.... it appears that there is an error in your configuration... have you tried a `git revert`?"


I'm not being dismissive. Well, I am dismissing a lot of industry people's opinions. Because they're bad.

Just because people decide stuff for money doesn't mean I can't call them bad. Not everyone is equally skilled.

And your parable is exactly the issue. The unskilled and loud and whiny do often win, and it's a shame. I see it all the time.

(Also you're way overstating Nix as a "full blown FP language." It isn't hard to learn. I learned it just be existing on a project with it. Within 6mo, now I'm apparently a "Nix expert" and people now point at me as one of the people who "knows it" and "you can't expect everyone to know it like you do." idk maybe I'm some genius but I think it's more that I just don't have a bad personality.)


So you are being dismissive. That's what you're doing. You're dismissing more than just "stuff for money". You're dismissing anything that doesn't fall under the "skill" or "technical" category. All software projects contain a human element. I was showing an example from my experience on how something like that could happen.

> A perfectly capably (but perhaps a bit esoteric) technology is picked by a smart passionate person for a project.

> The novel technology is in 1 isolated module that's mostly feature complete for the first 1-3 years.

> People in the team/company deal with that "thing" as a blackbox more and more

> 5-10 years later, mostly new team maintaining the project. They hate the weird choice of tech. "Why is only this one component different???"

> People understand the contract with the "black box" very well, but HATE the "black box". People think "We can implement the black box contract very easily"


Two things can be true in your hypothetical:

1. Someone was forced to maintain Nix and want to switch to easier to maintain tooling 2. That someone can lack in technical understanding of the problems they are facing

The former doesn't negate the later.

The way I would put it is sometimes you choose a worse option because the people you have available are better at that option. That doesn't mean you made a mistake but it does mean your lack of expertise sent you down a different path.

And of course to finalize I will re-emphasize my "didn't make a mistake" comment. Ivory tower isn't a good idea either.

But someone responding "it is too bad the company that built packages couldn't properly use the package tooling they depended on" can still be true in a situation where a company made the correct decision of dropping that package tooling.


Yes I am dismissing. People don't have a right to not be dismissed if I judge them poorly. People are allowed to have bad professional opinions of others.

And I am dismissing the types you describe specifically. I dismiss them (privately amongst the likeminded) at work all the time too. I just put them on a list in my head when they start spouting these sorts of bad values.


> maintaining 3 stacks, a Nix stack, a Go stack, and a Rust stack

Nix is a package management system with a little bit of programming tucked onto the side. "Nix stack" is not the same type of thing as "Go/Rust stack". If you try to move things over to Go/Rust, you'll spend a little bit of time rewriting the code and a whole lot of time reinventing the wheel on everything else involved. You're not moving between implementations, you're building your own implementation. That's almost always a bad idea, and it's a much higher cost than learning the syntax.

Moving from a Nix stack to a Go stack only makes slightly more sense than moving from a docker stack to a Go stack. Which is to say, very little sense.


But they also have to manage the Git stack on top of all that! /s

(Though really, what's up with so many people in the industry being absolutely bad at git well into their careers?!)


Wow, this is literally the story of my team. Luckily there’s enough autonomy for us the escape the gravitational pull of the few remaining evangelists, but this is essentially what led us to this point.


I think that's kind of sad. There are a lot of good technologies that require some investment to understand them and a lot of worse is better approaches that end in a mayhem that is ultimately a terrible work environment.

For a while it seemed like investment would win but now I think we will have massive tombs of poorly written stuff that can only be worked around with even more LLM generated solutions. It is like the software bloat problem we've had recently but on steroids.


> Is this obvious, simple, and default behaviour?

In the documentation[0] they're right next to each other, and `buildImage`[1] (builds a single layer) specifically calls out that you probably want to use `buildLayeredImage` or `streamLayeredImage`[2] (both produce a separate layer per dependency) instead.

Neither should cause the final image to include build dependencies, that sounds like they were doing something silly like running `nix-build` from inside a Dockerfile and just taking that as their final image. Which.. yes, would include build cruft. Oh,[3] I guess that was exactly what they were doing after all. And mixing in Debian packages... for reasons, I guess.

[0]: https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-dockerTool...

[1]: https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerToo...

[2]: https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerToo...

[3]: https://github.com/railwayapp/nixpacks/blob/205b33b515282cdf...


> If Nixpkgs is the default and alternatives require additional research and effort then for most users it _is_ Nix.

This is not what parent commenter is getting at. Nix itself is a deterministic build tool. There is also a package manager built on top which uses a large collection of nix files to describe each package - this is nixpkgs.

They use the same primitives, but the same way you don't just yolo your node/rust etc build versions to whatever your OS comes with and use a lock file, you also want to have more control over the exact versions and thus may use something other than what nixpkgs packages. Especially that it makes it easy to override any property of your dependencies, unlike any other tool out there.


> Is this obvious, simple, and default behaviour?

Yes. There are two options IIRC, minimum layers and maximum layers (one per dep by default unless that makes too many, which is handled automatically) depending on what you want, and it’s a Boolean flag. If you need more control it’s more complicated but this one really is a strange criticism unless they’re using non-standard wrappers for the usual nix way to do this.


> If Nixpkgs is the default and alternatives require additional research and effort then for most users it _is_ Nix.

This feels rather dismissive. They wrote a bespoke solution, not a weekend toy. Surely you'd agree that they have more than just surface-level knowledge of Nix, to be able to distinguish between Nix and Nixpkgs? They're already doing non-trivial things by merging multiple commits of Nixpkgs in order to get different versions of different tools!

> Is this obvious, simple, and default behaviour?

Well, Nix doesn't do much of anything "by default", it's a pretty generic tool. But insofar as it matters, Yes, pretty much. `dockerTools.buildLayeredImage` will in fact automatically build a layered image, and it is the most "obvious" way (IMO) to build a docker image. There is also `dockerTools.buildImage` but there's no particular reason to use it unless you specifically want a flattened image. (The documentation available is clear enough about this. In fact, in practice, much of the time you'd probably actually want `dockerTools.streamLayeredImage` instead, which is also documented well enough, but that's beyond the point here.)

But that's not my point. As far as I know, Nixpacks don't even use this functionality, I'm pretty sure they wrote their own OCI image building tools. And in that sense, it is not obvious why they can't split the Nix store and the article doesn't explain it.

My point wasn't to be dismissive about the difficulties of Nix, it's that the blog post doesn't really do a good job of explaining things. It makes it sound like these are normal problems in Nix, but they are not; even the official Nixpkgs documentation often points to third party solutions for when you're working outside of Nixpkgs, since most of the Nixpkgs tools is geared for Nixpkgs and NixOS usage. As an example, take a look at this section of the Rust documentation in Nixpkgs:

https://github.com/NixOS/nixpkgs/blob/master/doc/languages-f...

So even if you're relatively new to Nix, as long as you are reading the documentation you will indeed definitely be aware of the fact that there is more to the Nix ecosystem than just Nixpkgs. It may not be surface-level Nix knowledge, but it's certainly close.


> While Nixpkgs is an amazing resource, Nix != Nixpkgs. Nixpkgs is highly unideal for cases where you want to be able to pull arbitrary versions of toolchains, but it is not the only way to go. For example, there is amazingly good Nix tooling for pulling an arbitrary version of Rust. Other Nix-based developer tools have shown how you can do this well.

It’s so exhausting that every single time a basic use issue comes up with nix, the response is “but there’s a way to work around it (that you need tribal knowledge for and will require writing dozens to hundreds of lines of code to fix in a language that doesn’t work like any of the mainstreams with bad error messages and poorly documented standard libraries)”.

People’s problems with nix are not that it isn’t turing-complete, it’s that it often creates more problems than it solves by refusing to provide a simple first-class API that interoperates with idiomatic projects in that ecosystem that just works.

If every project you try to use nix for devolves into centering around trying to fix issues with nix that you have to write your own modules for, why even bother using nix instead of mainstream tools with good documentation? Exactly what happened in this case. In most cases people are probably just deciding to use docker instead.

Nix’s refusal to address practical developer experience issues for a developer-facing product on a non-geologic timescale in favor of ideological pure flakes is rather frustrating.

Yes, people are contributing their own time, but it’s so damn frustrating to see so much technical effort going into something that’s rendered practically unusable because of bad UX.


You can use nixpkgs as a single versioned input, and you can also give certain things their own distinct input to be tracked separately in flake.lock, but I wouldn't describe either as a "workaround" for the other.

It is a bit of a headache to have to pick which mode you want for each dependency, but I'm not sure that's a headache that can be dispensed with via UX improvements.

> why even bother using nix instead of mainstream tools with good documentation?

nix lets you have a single source of truth for all of it. It's not nix vs apt or nix vs pip, but rather nix vs (pip & (apt|brew)).

So far as I know, the only other tool that scratches that itch is bazel, otherwise you're kind of stuck with multiple overlapping packaging strategies that don't communicate, which is a recipe for "works on my machine".


> It’s so exhausting that every single time a basic use issue comes up with nix, the response is “but there’s a way to work around it (that you need tribal knowledge for and will require writing dozens to hundreds of lines of code to fix in a language that doesn’t work like any of the mainstreams with bad error messages and poorly documented standard libraries)”.

> People’s problems with nix are not that it isn’t turing-complete, it’s that it often creates more problems than it solves by refusing to provide a simple first-class API that interoperates with idiomatic projects in that ecosystem that just works.

Well, first of all, this article isn't even about the UX of using Nix directly, it is about Nixpacks, a tool built on top of Nix. If Nix already solved the problems Nixpacks were trying to solve, they would've had no reason to write it, so I don't really see how this could be relevant.

> If every project you try to use nix for devolves into centering around trying to fix issues with nix that you have to write your own modules for, why even bother using nix instead of mainstream tools with good documentation? Exactly what happened in this case. In most cases people are probably just deciding to use docker instead.

Would it matter if there was good documentation if you were not willing to read it anyway? Both of the issues I talked about actually are covered in the official Nixpkgs documentation and have been for years. For example, here is some of the documentation for layered docker images:

https://nixos.org/manual/nixpkgs/stable/#ssec-pkgs-dockerToo...

Nixpkgs and NixOS are maybe not perfectly documented, but on the other hand, they're absolutely enormous projects. The documentation that does exist is already staggering. And of course it is! Seriously, try finding a singular project that has as big of a scope as Nixpkgs...

Of course, what people seem to read from what I said is that Nix is actually perfect and there are no problems, but what I'm really saying is they didn't go into very much detail on how they tried to solve the issues they ran into. The two possibilities are that they didn't try very much, or that they did and they omitted it. The latter is certainly as plausible, but it leads to this confusing problem where their complaints don't really make much sense on their own. What I am talking about is not obscure bespoke tribal knowledge. It's pretty close to the first stuff you would learn if you read docs or tutorials.

> Nix’s refusal to address practical developer experience issues for a developer-facing product on a non-geologic timescale in favor of ideological pure flakes is rather frustrating.

> Yes, people are contributing their own time, but it’s so damn frustrating to see so much technical effort going into something that’s rendered practically unusable because of bad UX.

This really seems like it's veering far away from the discussion about Nixpacks and into personal grievances about Nix, but honestly there's thousands of build systems and package managers that don't work the way Nix does, I do not really see why Nix should compromise its ideals. for the sake of UX. But please don't get me wrong: I agree on the point that Nix has a worse UX than it could; I think the language is full of unneeded papercuts and confusing things and meanwhile the time it has taken to stabilize flakes and the new Nix command has really been a drag and introduced a lot of confusion. On the other hand, though, I'm not even sure it's worth wasting too many tears over this: as hard as it is to get started in Nix and as many things as there are that could be improved with Nix and Nixpkgs itself, really the chief pain I feel when dealing with anything involving Nix is not Nix itself but the world it exists in.

Nix has existed for over 20 years and in those years the world of OS design and package managers only really started to move towards immutability and purity relatively recently. Most software still likes to have tons of mutable, impure shared state and trying to encapsulate this into Nix can be very painful. Nix has grown many limbs and improved in many ways to try to deal with this, but it is still far and away one of the biggest sources of confusion that I have with Nix.

It is not a given that things have to work this way, but it is a natural consequence of the fact that Nix is trying to do something that is very much at odds with the way software has worked for a very long time. The impedance mismatch is massive and unavoidable, but I don't think the answer is that Nix should bend to deal with this. If someone finds what they feel is a better sweet spot between what Nix is today and the world outside of Nix, they should feel free to pursue that, but I'm involved with Nix because I think it has the right ideals just way too early.

Having dealt with systems that try to be hermetic like Bazel, you can certainly get some meaningful benefits from sitting at a midway point, but what we're chasing are the benefits you get much closer to the end-game of hermetic systems, when hermeticity is enforced strongly across the entire system. This is about more than just being a convenient tool for developers and much more into the future of how systems are designed. And yeah, sure, when you try to build on top of this in today's world, it can be awkward, nobody is denying that, but a lot of this awkwardness is, unfortunately, a feature, and while clever Nix solutions may eventually exist for some of those problems (a lot of clever work is being done, with concepts like dynamic derivations) I really feel strongly that you shouldn't hold your breath.

And sure, by all means, switch to OCI tooling if you feel like it works better. It may very well actually be better for some use cases! But you literally can not replace most of what Nix is used for and can do with OCI tooling, it's ultimately a very small subset of what Nix is capable of overall.


I’m very sorry. But for a person that tries to get into Nix this reads like pure gaslighting. I haven’t even found an official description of what Nix vs NixOS vs nixpkgs tries to achieve vs what they do not want to achieve.

There is a near infinite collection of blog posts what people use Nix for. All full of Nix-internal jargon derisively critiquing alternative (that do in fact actually work) without ever coming out and laying the groundwork for what the underlying problem is _and what the solution to that is_.

I think that I would end up liking Nix in principle. I really did enjoy the Spack package management system and I do think Nix is doing things reasonably similarly (everything is Nix, all inputs are securely provided, reusing things as much as possible).y current problem is taking a homelab server into “production” and all I really want is a way to write down a state I want the services on it to be in, have that in a git repo and be able to regenerate this server after loosing it (in a first step without any data backup). I’m still, after 2 months (wall clock, call it maybe 3 days actually getting into this project), not sure if that’s a thing Nix tries to cover! (I think it does, but..)


A lot of work about packaging specifics reads like inside baseball because it is. Most people manage to avoid getting into the specifics of packaging because it’s stuff that mostly gets in the way of the problem they’re trying to solve. But for blessed few packaging is either critical to the problem or is the problem itself. If you never had to learn the inside baseball terminology, it is likely that packaging is not critical to problems that you solve, and I say that without judgment. If you need to get into Nix, you will. There are lots of reasonable ways to manage personal infrastructure that don’t involve Nix. For the problem you describe, I’m not even sure Nix is a preferable solution unless you already use it.

That said, I agree with the original comment. I’m willing to believe that they had these problems and that moving away from Nix was the right decision, but there is little detail in the explanation. They’ve been pretty closed users of Nix to my knowledge, building proprietary tools on top of it without contributing back significantly, and it feels like orgs such as repl.it are contributing back more actively, but this may be a marketing difference as well.


I spend significant parts of my PhD maintaining the packaging and deployment infrastructure of our group. But yes, I have close to no computer science and reasonably little intrinsic interest in the problem space (though I have a deep appreciation). Still I think I have a better than average understanding of the packaging space..


I think you may be overthinking things. In brief;

Nix is two things: a functional programming language called Nix, and a package manager written in C++ that compiles and installs packages written with Nix expressions. Nix the language itself, and it's implementation.

Nixpkgs is a repository of Nix packages. It also contains NixOS, which is also just a bunch of Nix.

NixOS is a Linux-based operating system built on Nix. It is configured using NixOS modules, a configuration system of sorts built on top of the Nix programming language. A NixOS system is literally a single big Nix package built from NixOS modules that depends on a bunch of other packages.

If this still seems too complicated it would probably be better to just dive in and mess with it so you can physically see what's going on.

> current problem is taking a homelab server into “production” and all I really want is a way to write down a state I want the services on it to be in, have that in a git repo and be able to regenerate this server after loosing it (in a first step without any data backup). I’m still, after 2 months (wall clock, call it maybe 3 days actually getting into this project), not sure if that’s a thing Nix tries to cover! (I think it does, but..)

Yes that does sound like something NixOS can cover. Obviously, you will still need backups for your mutable data (databases, files, etc.), but if you are able to encapsulate the entire setup of a server into a NixOS config you can indeed throw a machine out the window and have a new one set up and booting the exact same services in minutes.

This is pretty much the original thing that drew me into NixOS, especially because at the time I was highly interested in qemu GPU passthrough setups and I really liked how with Nix all of the complicated setup bits could be expressed in a single file, with about 20 lines of Nix, all accounted for and version controlled. A lot better than just hoping you remember to keep things documented.

I won't lie, learning NixOS and using it for a real machine setup was very painful for me at first. I struggled with many things over time. Still, I really think because the scope of things these projects do is huge it's really easy to overcomplicate things. All of the explosive scope of Nix comes from relatively simple primitives applied in large systems.

I really don't want to encourage people to suffer with Nix if they feel like it's just too convoluted and hard to work with. I am not trying to convey that Nix is simple or easy on the whole. I was only ever trying to explain why I felt the issues raised by this post were confusing to me, not trying to tell people that Nix is generally easy. I think a lot of replies genuinely continue to misunderstand this, but the point of saying that it's easy to do that in Nixpkgs is not to dunk on people who struggle with Nix, it's to say that it's not clear why Nixpacks would struggle with these particular issues. You can literally just split the /nix/store paths into separate OCI layers. There is no obvious reason why this strategy can't be used, and the blog post doesn't go into the background behind it. I bet there actually is background, it's just left unsaid.


I think I got the first part, including and up to

> If this still seems too complicated it would probably be better to just dive in and mess with it so you can physically see what's going on.

And it really seems like NixOS being _the_ correct :tm: solution for what I want from an home server. The problem is that Nix doesn't quite cover (or at least didn't when I first deeply looked) my needs (it lacked a useful Clickhouse package). And I don't even want to say "Nix should be simpler". I don't even think it _can_, it's fundamentally a complicated and complex problem to deal with!

My problem is solely with the Nix community. The amount of jargon-loving and argumentative "you shouldn't attempt what you are attempting in the first place" or "you are holding it wrong" gaslighting is really something :D

I think the point where you fail to grasp the problem is: Nobody is claiming that Nix can't do a thing. My complaint is: There is no way to find _the_ way to do the thing. And there seem to be multiple _incompatible_ ways to achieve any given state. Without any way to distinguish those (let's call them) camps for a new person. In this way it's everything Unix but dialled to eleven! You don't need to argue (for me) that anything _can_ be done. I'm getting that from "Turing complete". My problem is only the practice: How do I get there?

edit: I think my immediate problem is: How do I get the series of "commands" that a nix derivation will invoke? Kind of like "what's the receipt, that is being executed?" That's missing for a sensible repl-loop for me. I'm sure I'll be getting there. The capability that Nix offers is extremely appealing. But at the same time the documentation and community are unhelpful to the extreme


> My problem is solely with the Nix community. The amount of jargon-loving and argumentative "you shouldn't attempt what you are attempting in the first place" or "you are holding it wrong" gaslighting is really something :D

> I think the point where you fail to grasp the problem is: Nobody is claiming that Nix can't do a thing. My complaint is: There is no way to find _the_ way to do the thing. And there seem to be multiple _incompatible_ ways to achieve any given state. Without any way to distinguish those (let's call them) camps for a new person. In this way it's everything Unix but dialled to eleven! You don't need to argue (for me) that anything _can_ be done. I'm getting that from "Turing complete". My problem is only the practice: How do I get there?

I honestly have relatively little experience with the majority of the Nix community, so I can't speak too much to it. I did not personally experience too many problems.

The frustrating thing to me about this thread is that it definitely wasn't in this vein. I know I'm harping on this a lot, but it matters.

What the blog post says is "we can't split the image into multiple layers with Nix". And again, they're doing their own OCI building, so this actually has little to do with Nixpkgs and is much more conceptual in nature. If you build a Nix closure, what you wind up with is a number of store paths. For example, for bash, you might have the following store paths:

    /nix/store/za53jjhjl1xajv3y1zpjvr9mh4w0c1ay-xgcc-14.2.1.20250322-libgcc
    /nix/store/yypqcvqhnv8y4zpicgxdigp3giq81gzb-libunistring-1.3
    /nix/store/6nkqdqzpa75514lhglgnjs5k4dklw4sb-libidn2-2.3.8
    /nix/store/cg9s562sa33k78m63njfn1rw47dp9z0i-glibc-2.40-66
    /nix/store/xy4jjgw87sbgwylm5kn047d9gkbhsr9x-bash-5.2p37
So how do you split these into layers? Well, however you want. You can place them into individual layers, or you can group them based on dependencies to make more "logical" groupings and get less layers, or basically any number of things. There's plenty of metadata on what depends on what, so it's really up to you to decide how to do that.

I was not talking to end users when I said this, I was talking to developers building on Nix that almost definitely knew this. So it's unclear based on what they said why they couldn't.

> edit: I think my immediate problem is: How do I get the series of "commands" that a nix derivation will invoke? Kind of like "what's the receipt, that is being executed?" That's missing for a sensible repl-loop for me. I'm sure I'll be getting there. The capability that Nix offers is extremely appealing. But at the same time the documentation and community are unhelpful to the extreme

I see.

Well, for starters, you are trying to start at the very low-level bottom of the stack. I understand this because it is the approach I prefer to take when learning new technology, but I'm telling you right now this is a hard road to go down with Nix, I've been down it. The documentation is going to continue to feel unhelpful because you will find plenty of documentation that is happy to take you down the dark paths of how things actually work, but that doesn't make them any easier to navigate. It's much easier to start by asking "how do I accomplish this end-goal" than to ask "what actually is a derivation".

With that out of the way... to understand what's going on with builds, you really genuinely do need to know what a derivation is, because it matters. Almost nobody calls the `derivation` function directly in Nix, instead using Nixpkgs mkDerivation tool at the bare minimum, but at the very bottom, there is only one function that matters:

https://nix.dev/manual/nix/2.22/language/derivations

At the bottom, in order to actually build something, a derivation has to be evaluated and realized. Internally, instantiating a derivation will cause it to be built. It is built by taking the parameters passed to derivation and serializing them to an internal format, then "instantiating" it, causing it to be built inside of the sandbox. The internal derivation format looks like this:

    Derive([("debug","/nix/store/xsxanvyy93g3k5dnh0j2wisrwvq012b2-bash-5.2p37-debug","",""),("dev","/nix/store/lnzar8zawj43wkqlsdqhywda9hjymrgf-bash-5.2p37-dev","",""),...
Basically a lot of arrays and tuples and whatnot. Not very easy to read. `nix derivation show` can give us better human-readable output:

    {
      "/nix/store/rfkzz952hz2d58d90mscxvk87v5wa5bz-bash-5.2p37.drv": {
        "args": [
          "-e",
          "/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh",
          "/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"
        ],
        "builder": "/nix/store/razasrvdg7ckplfmvdxv4ia3wbayr94s-bootstrap-tools/bin/bash",
        "env": {
          "NIX_CFLAGS_COMPILE": ...
So what does `nix build nixpkgs#bash` do? Well, aside from the sandbox setup, on my particular machine, it would run... /nix/store/razasrvdg7ckplfmvdxv4ia3wbayr94s-bootstrap-tools/bin/bash -e /nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh. So, basically, a bash script. What does that bash script look like? Here's the entire contents of default-builder:

    genericBuild
This is in the heart of the Nixpkgs "stdenv" machinery, and now we're outside of what Nix itself does and into what Nixpkgs does. Literally all Nix does is handle the book-keeping: sandboxing, execing the builder, handling the Nix store; for the actual build, it is just execing the builder, and in most cases, it will wind up being handled by a bash script. You could have whatever builder you want here, of course, and it needn't be bash or bash-based. (Note that we're calling bash to build bash - funny choice for me to pick, but you can see how it gets the copy of bash to build bash with from a special derivation called bootstrap-tools. Gotta break the cycle somehow, and Nix doesn't have as impressive of a bootstrap seed as Guix.)

So, for this build, it just calls genericBuild. What does that do? Well, you can keep chasing down the scripting here, but ultimately it calls all of the build phases, e.g. unpackPhase, patchPhase, configurePhase, buildPhase, checkPhase, etc. which are all just bash scripts inside of environment variables. At the end of the day, it does roughly what you would do to build bash, plus some patches. The nixpkgs stdenv automation helps handle many special Nix things, but it's not actually horribly complicated.

By the way, when you see this sort of thing in a Nixpkgs derivation:

    patchPhase = '''
        ...
    ''';

It is in fact just setting up environment variables. You can see them in `nix derivation show`, even. They get called just by substituting them directly into a command, not any different than:

    test="echo Hello world" bash -c "$test"
Basically, all of the attributes in the mkDerivation call become environment variables, including src, and all of the magic that gives them meaning is just in the stdenv default builder. (This is roughly true, anyway. I think there is some move to make it "stricter" but the default way it works is still like this roughly.)

So how can you debug what actually happens when building a derivation? Unfortunately it's not terribly easy. You can view the logs with `nix log`, but it will only show one if it actually built it, not if it substituted it from Hydra cache for example. Also, the default behavior of the builder doesn't output that much diagnostics, though you can add NIX_DEBUG = 7 in a derivation and it will output tons of information about what is being executed.

The way derivations depend on other derivations is something like this:

- During build-time, when you try to coerce the result of the derivation function to a string, what gets returned is the realized store path. So if I try to do `${pkg.bash}/bin/bash` inside of Nix, it might return `/nix/store/xy4jjgw87sbgwylm5kn047d9gkbhsr9x-bash-5.2p37/bin/bash`. In this way, it is natural to literally just depend on something implicitly by realizing it as needed.

- As far as I know, for runtime dependencies, it is detected by literally scanning the $out directory after a derivation is built and looking for the Nix store paths of other derivations. Merely realizing a dependency during build-time won't make it a runtime dependency.

It's more complicated than this, but that's the broad strokes. So when you build bash, it will implicitly depend on all kinds of build tools like GCC and make, but the resulting derivation will only depend on things like libgcc that are actually referenced in the final derivation.

Honestly, it is a bit of a black box. It is not easy to navigate. Trying to learn Nix from the ground up first will be very hard. I wouldn't ever tell someone not to try, but I will definitely say it won't be easy.


I think the part that's easy to miss is that their users are devs who will want to specify their own dependencies and versions for arbitrary packages.

The way nix works with the way nixpkgs is structured, pinning a version of any package means pinning a commit of the entire nixpkgs tree. Since package builds of node/python/ruby packages depend on stuff outside of the package dir in the tree, you need that mapping between versions and commits. It is also a leaky abstraction, so they will need to expose that to their users, who now may run into situations where they need to align various states of the nixpkgs repo when they just wanted to "yarn add new-fancy-nodejs-package-with-linked–native-deps".

Using nix without nixpkgs may be fine for more scoped use but seems hard to justify for a platform like Railway.


> Since package builds of node/python/ruby packages depend on stuff outside of the package dir in the tree, you need that mapping between versions and commits.

> Using nix without nixpkgs may be fine for more scoped use but seems hard to justify for a platform like Railway.

Nixpkgs isn't all-or-nothing. You're right that Nixpkgs itself rarely packages more than one version of something, but the standard approach for "language package managers" is that you use a tool like crate2nix[0] which automatically generates pinned derivations for all of your dependencies.[1] For system dependencies which aren't covered by your language package manager.. you're basically in the same position as for something like Debian: you can either pull it from Nixpkgs (and give up control beyond "which Nixpkgs am I on?") or you can write/fork your own package. Or you can pull specific packages from specific Nixpkgs checkouts and splice them into your "main" Nixpkgs version as an overlay (though this is definitely getting into the Weird Territory(tm)).

[0]: https://github.com/nix-community/crate2nix

[1]: For example: https://github.com/stackabletech/secret-operator/blob/30f0eb...


> The way nix works with the way nixpkgs is structured, pinning a version of any package means pinning a commit of the entire nixpkgs tree.

You can do that for a single package. For example, the latest version of linux-firmware is currently broken for my wifi cards so I pinned linux-firmware to commit dda3dcd3f of nixpkgs, whereas the rest of my software comes from the latest version of nixpkgs. There are multiple ways to accomplish this, but the way I did it was:

1. Add an input of a specific version of nixpkgs (which I called `nixpkgs-dda3dcd3f`): https://code.fizz.buzz/talexander/machine_setup/src/commit/e...

2. Add an "overlay" which replaces linux-firmware in the regular nixpkgs with the copy from nixpkgs-dda3dcd3f: https://code.fizz.buzz/talexander/machine_setup/src/commit/e...


> Nix != Nixpkgs

I've been told this when trying FreeBSD in regards to freebsd ports. pkg generally works fine for me, but one day I tried to go off the beaten path and compile vim with some custom USE flags (I forget what they are called in freebsd) in the ports section. It pulled down 20+ dependencies and each `make menuconfig` kept asking me "would you like any of these options" I selected a few that seemed reasonable, and lo-and-behold, package 16 out of 23 fails because "this-requires-that and that-needs-Fubar3.32.1 and Fubar3 is deprecated for Fubar4" and I just gave up. I get that the Core OS devs can't support all 10k+ packages, but they should also be very clear that if you actually try to use them (i.e. enable custom features, not just compile stock code) there's a high chance they won't work. Another option would be to yank them from the ports list if they don't compile and require at least some standard, independently-produced build to succeed before they appear in portsnap fetch.


I think this is pretty well stated. I'll add that while nixpkgs isn't nix, nixpkgs is kind of the good part. I use NixOS and for the first time in my life, I'm using the latest version of the Linux kernel on release day. That's pretty excellent. While I've come to tolerate Debian Stable in my old age, it is always like stepping a few years into the past ;)

The Nix language is something I could criticize for hours without getting bored, but it is what it is. It's old and they did the best they could and it's probably not worth changing. The Nix build system feels awfully primitive to me, often rebuilding stuff that doesn't need to be rebuilt for no good reason. (For example, my NixOS installer ISO has a ton of the build depend on the cmdline I pass to the kernel [just console=ttyS2,1500000n8], and so changing the speed of my serial port requires about 3 minutes of build time. It's goofy and makes me laugh, I'm not going to stop using Nix because of it... but it's also something that I wouldn't let happen in MY build.)

Nix for Docker images is, in my opinion, what it's the worst at. A long time ago, I was writing some software in Go and needed to add the pg_dump binary from Postgres to my container image. The infrastructure team suggested using Nix, which I did, but our images blew up from 50MB of our compressed go binary to 1.5GB of God Knows What. pg_dump is 464K. I ended up doing things my way, with Bazel and rules_debian to install apt packages, and the result (on top of distroless) was much cleaner and more compact. My opinion with some actual Nix experience is that a Nix system always ends up being 1.4GB. My installer ISO is 1.4GB. My freshly installed machine is 1.4GB. That's just how it is, for whatever reason.

Finally, the whole "I would like to build a large C++ project" situation is a well worn path. s/C++/Rust doesn't change anything material. There are build systems that exist to make the library situation more tolerable. They are all as complicated as Nix, but some work much better for this use case. Nix is trying to be a build system for building other people's software, supporting nixpkgs, and lands on the very generic side of things. Build systems that are designed for building your software tend to do better at that job. Personally, I'm happy with Bazel and probably wouldn't use anything else (except "go build" for go-only projects), but there are many, many, many other options. 99% of the time, you should use that instead of Nix (and write a flake so people can install the latest version of Your Thing with home-manager; or maybe I'm just the only person that uses their own software day to day and you don't actually need to do that...)


> a Nix system always ends up being 1.4GB

That's strange, I never had problems building really tiny docker (release) images with nix, in fact it felt easier than doing it with alpine. You just get exactly what you specify, no more.

(OTOH, when developing in nix, I always end up with a huge /nix/store and have no idea how to clean it without garbage collecting everything and having to wait all over)


> I always end up with a huge /nix/store and have no idea how to clean it without garbage collecting everything and having to wait all over

FYI you can avoid things getting garbage-collected by doing `nix-store --add-root`; that makes an "(indirect) garbage collector root"[0]. Especially useful if you're using import-from-derivation, since that imported derivation won't appear in the dependencies of your final build output (which, to be clear, is a good thing; since it lets us calculate a derivation, e.g. by solving dependency constraints or whatever, without affecting the eventual hash if that calculation happens to match a previous one!)

[0] https://nix.dev/manual/nix/2.18/package-management/garbage-c...


Honestly this feels more like rail... wants to make their own version, hence a new railX lol


Correct


I don't understand the versioning argument. New to nix, but I definitely have packages from a specific commit.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: