Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I have recently adopted Nix and NixOS and it's been truly fantastic. It does have a really harsh learning curve though. Big downsides, but for me the upsides are so huge that it's worth it.

I had been incrementally building this complex workflow using mkosi. At some point I sat down with a pencil and asked myself "ok, what am I building here, what's the ideal endgame?" I sketched out the system I wanted and pondered it for a bit... And then went "wait, have I just described NixOS?" And yeah basically it's exactly what I wanted and more.

Funnily enough, this article is _exactly_ what I need as I haven't exactly figured out the best way to install NixOS on a totally blank machine. (So far I have been muddling through what's described in the "Manual installation" section and, yeah it sucks). The exception is for Raspberry Pi where it's straightforward to just build an SD card image from a flake.

I had _guessed_ that something like the "Building your own installer" section would work, but because of the nature of Nix I never really feel 100% confident that something is gonna work as I hope until I physically try it. Usually it does. But still, this is a very big downside of Nix!



Another option is to run it ephemerally from the RAM. I have been doing this for my laptop and servers for years now. You can do so by declaring the following: https://github.com/ponkila/HomestakerOS/blob/main/nixosModul...

You need to then have a flake which defines a "package" like this: https://github.com/jhvst/nix-config/blob/main/flake.nix#L117...

Now you can run 'nix build .#starlabs' which will create you the files needed for kexec / PXE booting.

You can also write tests that these images actually boot ok: https://discourse.nixos.org/t/nixostest-with-flake-configura...

I started working on this stuff because I needed to somehow reliably update my homeserver. I live abroad 50 weeks a year, so I needed something reliable because I can't assume I have physical access to the computer. Even when the BMC firmware got screwed by ASRock motherboard firmware, I didn't need to sweat.

Now for the shameless plug: my company created a PXE server module which also supports flakes that assumes this ephemeral setup. It's described in here: https://github.com/majbacka-labs/nixos.fi


Ah yes this also looks fantastic and something I intend to try.

At $work we have insane platforms that are extremely hostile to platform development but they do support a (obviously annoyingly custom and limited) PXE-like thing and I'm hoping I can boot NixOS via that and just never even touch the disk (which is full of insane custom $work platform bullshit).


> I never really feel 100% confident that something is gonna work as I hope until I physically try it

I ended up in the same situation too, but discovered relatively quickly that NixOS makes it really easy to build clean VMs from your config that you can run and test against, then when validated, you apply the configuration to the actual hardware.

I'm doing this for all my servers managed with NixOS right now, and managed to prevent multiple issues being deployed, including something that would prevent a clean boot and since the host is headless, would have required me to connect monitor and keyboard in order to recover.

Really glad NixOS is setup the way it is, although you're right the learning curve is rough. At least I can easily re-install my infrastructure really trivially, which helps me sleep at night.


I’m using that exact feature for a blue/green/another color deploy setup, it’s very nice. The server with the hosted application builds a vm of itself, run some checks. Route traffic to (RR). If it’s all good, send the VM to a secondary house and route to there. On the primary, reboot (usually kexec). Switch back over.

Should probably just be using a persistent but this is some pretty heterogenous hardware and…it works


Yeah I make extensive use of this and it's absolutely fantastic.

I can write a config, very quickly check it seems correct on a QEMU guest, and then deploy "the same" config to a physical host (slower to boot, harder to debug, annoying to power cycle) and be highly confident it's gonna behave like the VM did. This is incredibly valuable!

But by "hard to know if it's gonna work" I was talking more about architecture-level concerns. Not "is my Nix code correct" but "is this thing I'm envisaging a thing that the ecosystem supports?" I usually just have to sit down and try coding it up, and there's always an element of uncertainty about whether I'll succeed. Whereas if I sit down to get something done in Ansible I don't really have any such doubts. (But the result will be dramatically worse).


Regarding how a bunch of the pieces go together, I found this blog post from Shopify quite useful: https://shopify.engineering/what-is-nix


how do you do this and what do you use? do you just manually trigger qemu? I'd love to be able to boot VM with different nix configurations.


Usually I just use `nix run .#nixosConfigurations.<my config>.config.system.build.vm`

This boots directly into the kernel with QEMU's '-kernel' option, and it mounts the host Nix store into the guest via Plan 9 FS. So there is some significant difference vs a full system, e.g. it's not possible to nixos-rebuild the VM. But if you're just testing high-level stuff it's totally fine.

Then there's also a .vmWithBootloader variant which behaves a bit more like a physical machine, IIUC it builds a disk image and boots it via Nixos' actual bootloader.


> I had _guessed_ that something like the "Building your own installer" section would work, but because of the nature of Nix I never really feel 100% confident that something is gonna work as I hope until I physically try it. Usually it does.

One of the most underrated features of Nix imo is the testing story. You absolutely can be confident that something will work!

https://nixcademy.com/posts/nixos-integration-tests/


Yea I agree on that front. But I don't mean "it's hard to write correct Nix configs" I mean that without a lot of experience it's hard to design a Nix system in your head and know whether the design is naturally supported by the ecosystem or if you're kinda going off into uncharted territory.

It's usually pretty easy to try stuff and I really like the way once you have learned something, you can't ever really forget it because everything is code. I know lots of things say "it's config-as-code!" but very few do hermeticity as well as Nix. In that regard it has some of the benefits of monorepo life, without needing an actual monorepo.


I dtarted my Linux journey 3 weeks ago with Arch and Ansible to create a declarative system. The installing-part however was tricky, because I have a second disk with Windows and I want to use secure boot. And if I wanted to use Ansible, I would have to create a specific ISO. And then I would have to manually define the disk I want to use or run into the risk of formatting the wrong one and lose a lot of data.

Then I found NixOS, which is like Ansible, but better, because Ansible only adds information to a system, but NixOS also ensures that anything that is not declared is removed as well.

After three weeks, I realized that the effort to declaratively (or automate an imperstively) install the OS is such a lot of work that does not pay off if you're not going to install multiple systems each week or month.

How many commands does it take to actually install NixOS? You partition the disk which take 3 commands. Then you format it, which takes 3 commands. Then you mount the disks, which takes 3 commands. Then you install it, download your configuration and apply it, which is another 2-3 commands.

It takes 5 minutes vs whatever effort you put into creating a declarative installer and keeping that up to date. I also had to think of the time it would cost me to actually figure out HOW a declarative installation would work and the tools I would require. I reckon that the first 24 system would essentially be free (opposed to the 2 hours or so it would cost me to figure out declarative OS installations).

I decided that for me, it is not worth it, although I am the type of personality that just hates not automating things.


Declarative management of your OS is in terms of effort basically an inversion of imperative management.

With declarative, you frontload an enormous amount of effort:

- You have to learn your declarative framework

- Then you have to slowly build your configuration base, or copy someone else's and figure out why they did what.

- Then you have to figure out workarounds for things that aren't natively managed declaratively.

- Then you start writing your own little helper functions and modularize things. At this point, it starts getting very smooth.

From there on, adding another package or another machine is very very easy, basically adding a few lines of code. And now your system is mostly reproducible, putting you far ahead of an imperative configuration.

Compared to that, doing it imperatively you can just sort of do your thing and roll with it. This let's you do whatever you aim to achieve very quickly, but as time goes on you have to juggle so many balls in terms of tracking and remembering what you configured / what state your config is in that it quickly becomes impossible.

Declarative OS management really sticks once you accept that the first few weeks are gonna suck and the first few months are still gonna have friction. But once you get over the hump, the rewards are there to reap.


I feel like a lot of that effort could be reduced though:

> You have to learn your declarative framework

Could the framework be simplified so that you don't need to learn all of it but rather just a small part when you're only doing basic stuff?

> Then you have to slowly build your configuration base, or copy someone else's and figure out why they did what.

Couldn't pre-made configurations be made available? For example in docker it's very easy to base your image on another image from an online registry.

> Then you have to figure out workarounds for things that aren't natively managed declaratively.

This is unfortunately a consequence of the current ecosystem essentially being imperative-first, but I wouldn't consider it an intrinsic limitation of declarative systems.

> Then you start writing your own little helper functions and modularize things. At this point, it starts getting very smooth.

This is probably the part where you really need to spend a lot of effort, but I wonder if it's possible to reduce the need for these helper functions and modularization or provide them by default for at least most common cases. Then most wouldn't need to spend the effort for them because they either already exist or there would be no need for them.


> Could the framework be simplified so that you don't need to learn all of it but rather just a small part when you're only doing basic stuff?

If you just want to do 'basic stuff', declarative OS management has way too much overhead.

> Couldn't pre-made configurations be made available? For example in docker it's very easy to base your image on another image from an online registry.

Nix really is as a configuration of configurations (called modules). So yes. But no one runs the exact same OS setup or container, so eventually you will need to modify it to your specification.

> This is probably the part where you really need to spend a lot of effort, but I wonder if it's possible to reduce the need for these helper functions and modularization or provide them by default for at least most common cases

Nix already has premade helper functions for common cases via 'builtins'. As far as modularization goes, at some point you are going to want to do something custom, and if you want to apply that to multiple systems then making it a module just makes sense.


The crazy bit is pretty much everything you’ve described exists. Except for the most important bit, the documentation.


> Declarative OS management really sticks once you accept that the first few weeks are gonna suck and the first few months are still gonna have friction. But once you get over the hump, the rewards are there to reap.

Only if you don't change your mind and do not decide to try out that new cool distro that just came out 6 months later.

I like declarative management but so far I have sticked to puppet for that. Sure like with ansible puppet needs to be installed first but on the other hand you can quite easily convert/reuse a config for say debian, to ubuntu, arch or fedora. The main differences are usually package names.


Lots of people don't realize it, but you can actually use Nix for packages and package config on any distro and even macOS.


Welcome to Linux!

I agree that through the lens of “how much time do you save?”, automating a NixOS installation is not worth it. As you describe, it’s just a few interactive commands in the upstream installer.

But from the perspective of “how much effort is it to spin up a new VM for this new project / task?”, spending the extra few minutes on building the fully automated installation path is well worth it. Also consider the perspective of “how many steps does it take to recover this VM in a disaster scenario?”, where reducing the manual steps to a minimum is very helpful.

BTW, the maintenance of the installer is virtually free: The configuration I show is the configuration I use in NixOS as well, so that needs to be updated anyway. Aside from that, to rebase my installer from NixOS 24.11 to NixOS 25.05, I just changed a number. When setting up a new machine, I can either download the upstream installer and write it to a USB stick, or I can change a number, rebuild (< 2 minutes) and write to a USB stick. Really not much difference.


Yeah maybe I should have mentioned that despite my enthusiasm I haven't bothered to install NixOS on my laptop or anything! I might one day, but that will be "for the love of the game" not because I expect it to save me any time. Until then, an incredibly basic dotfiles manager is perfectly good enough.

But for setting up appliances, test machines, it's perfect. If I ran cloud VMs I'd also consider it for setting those up.


> It does have a really harsh learning curve though.

Would it be possible to automatically translate all Nix build scripts into a language that is more ergonomic and fun to work with?


Nix guarantees that a given derivation will always evaluate to the same store .drv (the 'recipe' that is actually built) if the derivation itself (or anything it depends on, passed as arguments) does not change.

This property requires a pure language with no side-effects. Nix was not completely pure (e.g. `currentSystem` and `getEnv` were impure), but Flakes close this gap.

You could of course generate Nix from another language (or .drvs), but you probably want to keep the same guarantees.

Besides that, Nix itself is actually a pretty simple functional programming language. The difficulty is probably for most people knowing/understanding everything that is built on top of it: fixed points, overlays, nixpkgs.lib, stdenv, overriding, hooks, all the programming-language specific builders, etc. It is a pretty big ecosystem and a lot of it is not very well documented.

That said, when I started using nixpkgs/NixOS in 2018, it was the easiest package ecosystem to start contributing too. RPM .spec and Debian rules files are quite archaic, nixpkgs was really a breath of fresh air. It's just that because NixOS is very principled and does not follow HFS, you have to become a packager yourself much sooner than most other distributions to get stuff working. (Though this changed a bit with nix-ld.)


As others have said, this would be a solution to the wrong problem. The language itself is fine. I just read through the language intro on https://nix.dev and immediately just "got it" - if you've done any functional programming at all and/or used stuff like JSonnet/Cue there's not much to learn.

But also the fundamental model of Nix is incompatible with anything that can be translated to a simple Bash script. If you could translate it to Bash I wouldn't wanna use it

But yeah the difficulty is the ecosystem. "Nix" is a lot of things and all of those things are individually extremely flexible. This adds up to a system that can do almost anything and that inevitably makes it kinda hard to break into. The docs are honestly better than I could do but documenting this system just seems unbelievably hard to do well.


The learning curve isn't due to the language, and personally I find writing nix files to be much more pleasant than any other build system I've worked with, and most languages that I've worked with.


Absolutely this. Everyone fixates on the language but the hard part is really the packaging ecosystem and how-do-I-X type questions where Nix has ten answers that are all better than what Debian or Arch can do, but it's not obvious which is the best between them or how they compare.

See things like Python packaging, how to make ISOs/installers/containers, how to override a scope, how to maintain a dev environment, how to do package testing, how to set up your own CI with caching and binary delivery, etc.


Yea, IME the best manual for nixos/nixpkgs is the repo and the manual for git, grep etc.

I clone or save every public repo or article or whatnot i find that helps me or seems interesting.

These two examples come to mind: the page that made what the config/options system really is [0], and the post that really drive home to me it’s all just configs! A config language all the way down! [1]

[0] https://nlewo.github.io/nixos-manual-sphinx/development/repl...

[1] https://astrid.tech/2022/11/03/0/overlay-nixpkgs-pr/


Yeah, and that's obviously pretty hostile when the best guide is reading the source.

I think the deeper problem that maybe isn't always considered in the "Nix needs better docs" discourse is that there are many tasks that simply don't have an optimal workflow in Nix (yet). It can be weird documenting how do some particular thing, but having to frontload that doc with a bunch of caveats about which particular set of tradeoffs the suggested approach is embracing.

For example with Python, a huge one is around how much you want to leverage the Python packages already in nixpkgs. If you want to use them, which makes sense if you don't really care about versions or you've got a lot of things with tie-ins to non-Python such as bindings packages, then you probably want the conventional buildPythonPackage workflow, where you manually package any missing deps, perhaps assisted by pip2nix: https://wiki.nixos.org/wiki/Python

But if you have a self-contained app that already has everything locked and you just want minimum friction, then you almost certainly want uv2nix: https://github.com/pyproject-nix

A lot of things in Nix are like this. Same story with containers where you've got dockerTools.buildImage but depending on your workflow nix2container is almost certainly the better choice. Nix's limitations around IFD are finally being addressed by https://tvix.dev/ but that's a rewrite and brings yet another potential split.

Nix is the epitome of being perpetually stuck in the 1000 flowers blooming [1] state of things, and in some ways that's beautiful, but it needs people to build a cathedral or two within the bazaar. Determinate [2] is trying, but they've also taken some heat from the community for concerns around OSS and governance.

[1]: https://news.ycombinator.com/item?id=10294833 / https://gigamonkeys.com/flowers/

[2]: https://determinate.systems/


That's an excellent way to put it! Also, never read the 'Let 1000 Flowers Bloom' article, thanks.

What I find extremely interesting, is I would give (and have given) exactly same advice to someone asking me about python or docker packaging. Which means that we both ended up with the same long struggles and rabbit holes independently. It would certainly be excellent if those cathedrals in the bazaar were more centrally known and documented, to concentrate efforts, and to help others avoid having to spend so much time doing the same.


The Nix world is not a huge place. I was largely self taught for about a year and then heavily influenced by working with Numtide, so a bunch of my tools experience comes from them.

And I actually landed a minor contribution to nix2container, which was nice to do: https://github.com/nlewo/nix2container/pull/77


Honestly just start "vibe coding".

After some discussion and iteration with Copilot I had a derivation that pulls the CSI protobuf spec and dumps python code and types into something LARPing as a real python library.

A few hours later I implemented my own CSI driver using these things.

Github crawls nixpkgs and everyone and their aunt have public nixos configs, the LLM is good at Nix.

That all nix build instructions are bash isn't the worst, and I hate bash. You can always create a python script to call in a derivation if you need.


Yeah I have had some luck with vibe coding Nix but also I've found you have to be extremely cautious. It bullshits a LOT more than with other coding things I use LLMs for. This has led me on a couple of wild goose chases.

I think it helps to have comments like

# AN AI WROTE THIS BIT, REUSE WITH CAUTION


It guided me to write the Nix expression to package the grpc libs, then it implemented a complete CSI interface with hostPath mounts that I could tweak afterwards. Also very good at making Kubernetes manifests, and CRD manifests and other config-like things.


No, because the Nix language is already ergonomic and fun. Any other language will make it worse.

It's a difficult problem domain that isn't solved with syntax sugar.


Agreed. Except for “with”. The with statement is definitely fun. And ergonomic. Until it bites you.


> I have recently adopted Nix and NixOS and it's been truly fantastic. It does have a really harsh learning curve though. Big downsides, but for me the upsides are so huge that it's worth it.

It's huge. I tried learning and exploring Nix Darwin. Went through at least half a dozen blog posts with "step by step" instructions. All different. All had some fatal error at some point even on a new install of macOS that was from moderate to impossible to find coherent help on.


I started with Nix darwin, and it's definitely buggier and less well supported compared to nix-proper. I'd recommend trying out building a NixOS VM for a better introduction to Nix.


Glad to hear that the article describes exactly what you need :)

Try out building an installer and run it in a VM, it only takes a few minutes!




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

Search: