NixOS is not for the impatient. Maintaining your system configuration is an exercise in software development like any other - if you lack rigor or discipline, your codebase will become painful to work with. Nothing is ever one shell incantation away - it has to be carefully architected into the existing system.
That being said, once you get the hang of things, you reap amazing benefits:
- You can clone your system to any machine, and immediately have an identical environment
- You can share system configurations as code (declare the means for hosting a website in its repository, for example)
- You can use a fully-fledged programming language to configure any part of your system
- You can make use of an extensive ecosystem of easily composable, prebuilt NixOS modules
- You can seamlessly integrate with Nix, allowing for ephemeral development environments and shells with packages, eliminating much of the need for imperative package management
- Everything in a Nix-based system must be derived strictly from (lockfiled) inputs, making the reproducibility guarantees incredibly strong (barring any network errors or resources being taken down)
- The declarative nature of anything Nix-based means that every change is documented - your system never shifts from the source of truth, compared to other distros where discipline is required to maintain reproducibility
- Nix is so robust that you could even nuke your filesystems on every log out, if you'd like
I can do (almost) all of these things with a traditional Linux distribution, git, and a few scripts written in my language of choice, and I can do it faster.
Nix seems more competitive with something like Conda or Spack than a traditional package manager like Apt or Pacman. Similarly, NixOS seems more suitable for deployment in containerized environments built on woefully outdated deps than personal dev setups with generally higher security requirements.
I've experimented with both Nix and NixOS, and while I adore both the beautifully functional language and the endlessly helpful community, I haven't been able to justify the overhead of maintaining a nix.conf, flakes or no flakes, on top of everything else I need to do on my machines. I kept running into issues where some program I needed wasn't already integrated with the Nix way of doing things or something that worked on my Linux setup failed to work on my macOS setup despite multiplatform guarantees.
Nevertheless, I wait with baited breath for the day that Nix replaces Conda among a critical mass of practitioners in my field. I've tried getting a few of my teams to switch (with help from Devbox and Devenv), but it's a struggle to get people to learn new things when their (often worse) way of doing things works well enough most of the time. Heck, I count myself lucky for getting some people to switch from the atrociously slow Conda to the significantly faster Mamba as even that was inordinately difficult despite for latter being a drop-in replacement for the former.
>I can do (almost) all of these things with a traditional Linux distribution, git, and a few scripts written in my language of choice, and I can do it faster.
As someone who moved from a directory of numbered bash scripts and GNU Stow to Nix, I can't disagree more. The fundamental difference is one is imperative and the other is declarative (and reproducible), and that makes all the difference.
Exactly. I have used Linux since 1994, from Slackware to RedHat to Debian to Gentoo to Ubuntu to Arch.
NixOS and nix-darwin both using nixpkgs is the first time I have the exact same shell and programming/CLI environment on all five of my personal and work machines which run Linux and macOS.
I upgraded them all to the 23.05 versions from 22.05 by updating my flake.lock and doing a rebuild switch on all machines. No regressions.
Some cobbled together shell scripts and a Git home dir management system (yadm) is what I had before and it doesn’t come close. I also dreaded upgrades with the previous approach. And didn’t have nearly the same control pristine replicated environment across machines.
It’s even flexible enough that on Linux desktop machines I have X11 and bspwm DE environment, on servers no X11, and on macOS it configures my system preferences, Dock icons and installed App Store apps.
Reproducible is key here. I remember trying to manage servers with Ansible, Chef and Puppet in the past but was always disappointed that trying to spin up a new server, or move a service between two usually failed. This is because service S1 forget to declare dependency D, but it happened to work because S2 needed the same dependency and declared it. Or simply that in the past S1 declared D but the config was "cleaned up" and it wasn't realized that D was still needed. (Or it wasn't needed at the time but then needed again, but you didn't declare it).
These types of problems are very rare in NixOS (although I do occasionally see it with the module system, but at least this is config dependent, not system-state dependent). If I apply the same config I have a very high confidence that I will get the same system. I lost my desktop's drive the other day and I was back fully functional within an hour. Most of the time was spent logging into Firefox, Email and various websites rather than installing the system. Managing servers is similarly easy, I would have a hard time moving to something else.
Intrinsic state (like user data or application databases) is still hard, but at least that is the only real concern. Better than dealing with state and config.
In a 'divergent' system (e.g. managed by bash scripts), the resulting system state might diverge from what the bash scripts have, because of manually running commands on the system.
In a 'convergent' system, the system tries to reach a target state by comparing what's there with what it's got.
In a 'congruent' system, the system is forcibly built to equal the target state.
The term 'reproducible' in 'reproducible build' (https://reproducible-builds.org/) conveys "bit-for-bit identical output". -- The benefit of that is security; others can compile the same sources and check that they get the same binaries.
But more generally, 'reproduce' just means 'create the same thing again'.
Nix uses "reproducible" in a more general sense: you provide the same inputs, and you'll get a program which behaves the same way, regardless of whether you compiled from source or downloaded binaries from a cache.
IMO, I think "reproducible build" is enough to unambiguously refer to the former. But, if you can think of a nicer word for "you get the same behavior from the same inputs" than "reproducible", it would be worth suggesting.
> I can do (almost) all of these things with a traditional Linux distribution, git, and a few scripts written in my language of choice, and I can do it faster.
Is this hypothetical? Or do you have a link you can share?
Many distros have been going through a difficult time just getting reproducible builds. To claim that you can do that (and more) on any traditional Linux distribution so easily is a bold statement.
Nix is also cross platform. I have a ZSH and NVim configuration from my NixOS laptop, and when I got a company MacBook, it was extremely simple to use that same configuration and get an identical setup.
That's two dotfiles and two directories each for my setup, which i just rsync over if I feel like. Are you talking about installing necessary depencies or something? I can't think of a ton of examples but maybe a language server? Not that it's strictly part of nvim. Some details perhaps?
Many times I’ve horked my system and rebuilding wasn’t quite the next thing I wanted to do. I’ve got a nixos server that I use for my home server and I’ve screwed it up so bad booting was difficult. However it’s super easy to just switch to a prior state. That alone is worth it.
A quick Litmus test: can you install Gnome and KDE, uninstall them and not have any leftover package?
None of your listed package managers can do that, and that’s not even a difficult thing like installing 2 firefox versions with only this deep dependency changed.
The thing with traditional package management though, these decisions are not that straightforward, especially for mega packages like KDE or LibreOffice.
When you install a window manager like say, sway, it will install a terminal, alacrity on some distros.
The distro specifically refrains from deleting alacrity when you're uninstalling sway and that's a reasonable thing to do even though people might disagree on it.
You can build build bash scripts that roll out your entire system from a full set of source from the ground up in about 5 minutes? I doubt your keyboard could even registers keys fast enough to type all that out.
I was thinking about words like this the other day - words that only occur in regular usage within a phrase. Such words sound odd when they aren’t used in their phrase. Of course I can’t think of any examples right now though
> compared to other distros where discipline is required to maintain reproducibility
You can do these things yourself, but it won't be standard, and it will be prone to error, and it will likely be inferior in many ways to a solution that is maintained and improved by countless people.
> You can clone your system to any machine, and immediately have an identical environment
It's arguably the killer feature of NixOS, if stability and purity means nothing to you.
A few weeks ago I bought a little SBC (Quartz64) for my homemade NAS project. Since I'm already a NixOS user, the bootstrap process was easy:
- Build ARM image for NixOS and boot off the device
- Clone my dotfiles and symlink my config folder into /etc/nixos
- Rebuild my system
And boom. Everything is there, my shell and coreutils and things I've come to expect all get rolled into the system. Updating it just means git pull and a system rebuild. As you say - it's not for the impatient. You have to maintain your config pretty regularly, and covering multiple devices across multiple architectures requires some deliberate config organization.
I'm not sure where I fall on the patience spectrum, but NixOS worked out pretty great for me. It's on my desktop, laptop and homeserver, and I haven't had a single bad update in my 8 months of daily driving it.
I use colmena[1] to manage one nixos configuration for multiple machines:
- laptop
- desktop
- server
- rpi nas
I also wipe my entire rootfs every boot with a zfs snapshot rollback[2] using the impermanence module[3] to keep specific stateful data one one of two datasets with regular snapshots: one is backed up with zfs send, the other is just for cache between reboots.
It took a little puzzling to get started, because I didn’t know about the impermanence module at first, so I built my own hacky solution. But I really love this setup. And the way I don’t have cruft to clean.
There are lots of ways to do this; pre-flakes e.g. you might have a root configuration file for each host which only includes other files, or have if statements.
Nix flakes lets you define as many configurations as you want, and (if you don't specify one) will select based on the hostname.
/etc/nixos/roles/foo/default.nix has config for the 'foo' role (eg, I have a desktop role that enables all my GUI options)
configuration.nix and hardware-configuration.nix in /etc/nixos/ are symlinked to the actual files in /etc/nixos/hosts/<hostname>/
in the imports section of each machine's configuration.nix, I import /etc/nixos/roles/foo for each role the machine should use. for your graphics card example, I have 'nvidia' and 'nvidia470' roles that pull in their respective nvidia driver (due to an old gaming laptop that requires the legacy driver version)
I have a 'core' role that all machines import, with the global config I want on every host
this allows me to version-control my entire /etc/nixos directory (managed as a private git repo, replicated using syncthing rather than pushed to Github/Gitlab/etc). the symlinks in /etc/nixos are in .gitignore because they're machine-specific, and the actual per-host config files are able to be tracked in their own directories.
There are a few solutions to this, but I keep separate configs for each device and link them into the entrypoint (/etc/nixos/configuration.nix) as-needed when I set things up. Then I have other "module" files for my terminal environment and my desktop environment, as well as an extra file that enables Wine/gaming packages.
If this setup could be combined with an easy way to customize things, like ublue (https://ublue.it/) lets you customzie fedora silverblue using Dockerfile configs, I'd adopt it in a heartbeat.
> Everything in a Nix-based system must be derived strictly from (lockfiled) inputs, making the reproducibility guarantees incredibly strong (barring any network errors or resources being taken down)
I thought "too complex, too much of a timesink" and that kept me from trying it for a long time. My experience is different.
If you are at all idiosyncratic about your setup, using Nix is better than whatever pile of bootstrapping bash scripts you've kludged together over the years.
Absolutely. I'm about a year deep on nix / NixOS and still spend way too much time refactoring things to be more DRY or what I think might be more idiomatic or maintainable going forward (as opposed to a more obvious but uglier approach). Definitely makes it easy to waste time prematurely optimizing, if you're prone to that sort of thing.
The difference is that with Nix, you'd always have a working configuration. With traditional deploy tools, scripts stop working at the most inconvenient timing because they're not reproducible.
This is keeping me from immediately jumping on Nix.
I'd rather use guix and scheme, but it's behind. If I learn nix, and then guix some day catches up, I'll already know nix and won't want to make the change anymore. Can't say I like the outlook for guix.
Interesting. I've been writing Bash scripts for over 20 years and despite my comfort with it, I'm always exploring avenues of replacing it. Despite the time investment already spent, I know there are far better tools out there to do the same jobs.
There's been plenty of comments describing the difference between those tools and Nix, and various descriptions and docs are a single search away, so the snide comment comes off as ignorant.
Most of us who use Nix came from one of those tools. I personally went from bash scripts --> Ansible --> Nix.
Nix is lightyears ahead of Ansible. Since I'm familiar with these two, I'll tell you what's wrong with them:
* They are imperative instead of declarative, so you have a ton of extra cognitive load figuring out the order of actions. I have thousands of lines of Nix config, change one file and it's not gonna break because something in another file was installed in the wrong order.
* They are not suited for managing the entire OS, but rather a layer on top of an existing OS. NixOS manages everything from the bottom up, so there are no loose ends to break you.
* They are not truly idempotent. You have to be very disciplined to keep them that way, including Ansible, despite their claims. My ansible deployments broke constantly
* They don't have rollback capability. If you mess up on nix, a single command or keystroke on boot will bring you back to the last working deployment.
* Their DSLs are not powerful enough of an abstraction. Nix is a fully fleshed out language so you don't have to write a bunch of boilerplate like Bash or need some special module like Ansible to make something happen.
* Git support is first class. You can compose your installation from several git repos, so say you want the cutting edge version of a package, you can install the one directly from the maintainer's repo instead of the one in the nixpkgs repo
* Speaking of which, Nix has more packages than any other distro. Key word here, DISTRO. Bash and Ansible aren't distros, and there is no central repo where anything you'd ever want to deploy is available and standardized and denormalized.
And a Nix deployment has persistent and writable storage, not sure where you get the idea that it doesn't.
Each and every point listed here could have be taken verbatim from a ten year old blog post why Puppet, or later Ansible, is the new great thing. Being declarative and idempotent is the basic design idea of these tools.
Managing every layer of infrastructure and the entire OS stack is also exactly what they aim for, and ironically also what many people regularly quote as why they haven't taken off more. Git support, and module versioning, is also completely on point. What is it that Nix does differently, and why? Is it a fundamentally different type of tool, such as Puppet is from plain Ruby scripts, or is it a better screwdriver?
That Nix is a distribution in itself is an important difference. But how does that affect the use case?
I realize the question have been asked before, but that's because not many attempts are ever made to explain where it sits in the problem space. It is not written for anyone who has experience with configuration management and infrastructure as code tooling, but instead for passive observers with the intention of bootstrapping the next hype cycle?
I explained specifically using multiple comparisons why it's different from Ansible and Puppet. Several of the guarantees made by Nix were never promised by Puppet and Ansible, such as enforced idempotency and managing the distribution from the ground up. These are fundamental differences, and not a better screwdriver. If you've ever used Puppet and Ansible, you'd know that an OS upgrade will break your scripts. I've had Puppet and Ansible break out from under me countless times. That is basically close to impossible with Nix, because the OS is not under it, it IS the OS. It fully manages anything that it deploys. Puppet and Ansible often just use external tooling like package managers or edit existing config files, which is grossly error prone. Nix IS the package manager, and it IS the config file. This is a huge difference. I understand that you can install a package from scratch and deploy entire config files using Puppet and Ansible, but this is side-stepping the underlying OS's capabilities, and this sort of discipline can only be maintained by a single experienced engineer, and not a large group of people updating the config, unlike Nix, which enforces deployment.
To say that Nix is a better screw driver is like saying that you could write the same level quality and reliability of software using Bash as in Rust. Rust is just a better screw driver than bash - I suppose it's true in some technical legal sense, but most people won't agree, and it's not true in any sense that is useful or valuable.
Again, it's an explanation only a passive bystander.
They're declarative, idempotent, and intended to manage infrastructure, operating systems and clusters, and coordinate application delivery, key rotation and configuration. Repeatability, rollback, and version control are the basic design tenets.
Sorry to hear that an OS upgrade broke your scripts once, but that's hardly the fault of configuration management tools in general or the Puppet/Ansible/Chef/Salt family in particular.
As you might tell from between the lines, I have some experience with these tools in various environments. But I also see the need for another generation, mainly because of the large shift to Kubernetes application delivery. The insistence of these tools to manage the entire OS stack makes people regard them as too complex for what they need. It is my personal belief that the concept of a host is limiting the continued acceptance of this type of tools and I have been spare time experimenting with other primitives to describe state on, such as binary file archives.
Playing with Nix, it is my impression that it is quite similar to other configuration management tools, a host is still the basic entity we serialize state on. State transitions are a linear graph.
What people describe as problematic with configuration management in general is usually perceived complexity, and Nix seems to have the potential for more of that. But I have not used Nix in anger, or in any type of real world complex environment, and I would be very happy to learn from someone who has experience.
> They're declarative, idempotent, and intended to manage infrastructure, operating systems and clusters, and coordinate application delivery, key rotation and configuration. Repeatability, rollback, and version control are the basic design tenets
That's what I'm saying, they are not declarative or idempotent in my experience, at least not Ansible. I'm not as familiar with Puppet so I wont say anything too confidently with that, but Ansible is 1. not idempotent as it allows you with ease to execute operations that can modify the system differently based on the system's state, and 2. not declarative, as the operations are step-wise.
Anyway sounds like you have experience and an interest in the field. My experience with Nix is more personal than professional but I'm happy to try to answer any questions you have about it.
The fact that such articles appear shows an untold problem of NixOS which is: complexity.
I've been a programmer and Linux user for many years, I know a lot of terminology behind it and I think it's a lot less of a problem for me to read technical documentation than for the average user. When I heard about NixOS I thought: "how awesome, it solves some of the problems that I have". But then I started to read official docs and dig into all of it and got quickly discouraged. It might solve "some of my problems" but at a time cost that I just couldn't afford. Well, maybe it's just not for me...
It's much easier for someone to be productive with VSCode without having to go out of their way to learn much.
Whereas, Emacs' requires much more effort to learn, even just to get started; but is much more extensible than VSCode is.
For the user who doesn't want to spend much time learning, surely VSCode is better. For the user who wants to get the most out of their editor, Emacs is a reasonable choice.
I think Nix is comparable. Much of the time, Nix is quite straightforward to use. But, when you do run into something that's hard, Nix can be very difficult to work with.
Maybe there'll be a tool that's to Nix/NixOS what VSCode is to Emacs; where you get the benefits of a declarative config.
How is emacs more extensible than vscode when vscode is an electron app in which you're free to do anything a webpage might? I struggle to think of any thing emacs or its plugins can do that vscode extensions can't, and I'd be willing to bet that the extensions are already available and not "you could do it with dev time"
I think parent poster refers to emacs being essentially a lisp interpreter, plus lisp itself being a literal AST spit out, enabling all kinds of extensibility
With VSCode, you've got your settings, and you've got the extensions you can install. With Emacs, the line between "plugin" and "configuration" is more blurred; you can write your own commands to cover how you do your work. -- It's more likely that an Emacs user has touched ELisp, than that a VSCode user has extended VSCode.
I recall running into a problem with VSCode extensions where you couldn't ensure one plugin would be loaded before another plugin. (e.g. loading the direnv plugin, before loading some other program which uses the loaded environment).
I thought the same thing for a long time. The reality is that I knew nothing about NixOS other than the sales pitch the day I installed it: nothing about the language, nothing about flakes or derivations or whatever. By the afternoon I had most of my setup replicated. By the next day I had both desktop and laptop running identical configurations.
When I got my MacBook I thought "hey it would be neat to install gcc and friends using Nix". I struggled through outdated documentation to add packages, mixed up editing the files and `nix-env --install`, struggled with the `nix search` command requiring flakes or something, and never did figure out how to even update my package versions. Earlier I also tried installing NixOS and never actually got any use out of the disk at all.
I’ll echo this sentiment. While it may take a while to figure out how to do something initially, the great thing about NixOS is that you only have to figure out how to do it exactly once. As far as my main desktop OS, I figured out a config that I liked several years ago, and haven’t really tweaked much since then. I ran the rolling release for a while, but I’ve moved back to the stable release, which is… well, stable.
As a programmer, nix (the package manager) has been saving grace for me. I use it in almost every project now and even in work to setup dev environments, ci builds, cross compiling and what not.
Complexity has both an essential and an accidental part. Nix is not (yet) as friendly as it could on the second part, but simply due to the problem domain it will have quite a bit of essential complexity, no matter what.
Package management is a complex problem and nix is the fist software that actually solves it, instead of just hiding it in the closet (e.g. docker-like solutions). If you want to deal with packages correctly (and I think we all want to), then this is the price.
Although it will improve on the accidental complexity part, e.g. currently many language ecosystems/software is not built with nix in mind.
AFAICT, you can get much of the benefit in a polished package without the pain by using Fedora Silverblue/Kinoite/Sericea for the OS and then creating dev sandboxes using either Fedora's toolbox or by harnessing the incredible Nix ecosystem using JetPack's devbox.
I haven't tried that combination. I'm currently using NixOS and loving part of it, but am bleeding on the sharp edges.
I'm currently using Fedora Kinoite with a few toolboxes (one for Rust, one for Node, etc). While not as fancy as a full Nix setup, it's an incredibly robust system that's modern and easy to maintain. A+ would recommend.
> I don’t use distros that lack a graphical installer (running fdisk is frankly beneath my dignity)
Ironically, I couldn't get the 22.11 graphical installer to work. I had to drop down to an Arch style install (which was fine).
My biggest complaint with NixOS is the pain of trying to run things that weren't built in nix (like Minecraft, or some other binary-only tool). I end up creating a podman image to run some of these things. I'm sure I could also create a derivation for them, but haven't bothered running down that rabbit hole yet.
If you're a programmer, Nix is awesome. But I don't think it's for the masses.
Random question, and yes, I’ll google it and ask GPT-4 or Bing (lol) but does anyone here know if it would be possible to run NixOS within WSL2 on a Windows 11 machine without having to dual boot and stuff? That would be incredibly useful for dev work.
Right now I’m using Ubuntu, and it’s awesome, with the obvious caveats, but I am Nix-curious for quite a while and would love to give it a spin in that way if it were reasonably doable. Especially to help friends who are less technical easily install all of the things, it could be a game changer.
There are some quirks, and WSL2 is buggy enough that I would advise advanced Linux users to prefer a bare metal install or a normal VM if those will work at all.
That said, I've used the linked NixOS image under WSL every day at work for about a year, and I've been very glad to have it.
Standalone Nix also works well under WSL2. I semi-regularly use it that way as well, on Ubuntu and openSUSE, for some testing at work.
Arguably the most important part of Nix is the fixed hashed output of everything, and these fixed /nix/store/hash-lib entries being used for dynamic linking.
That being said, once you get the hang of things, you reap amazing benefits:
- You can clone your system to any machine, and immediately have an identical environment
- You can share system configurations as code (declare the means for hosting a website in its repository, for example)
- You can use a fully-fledged programming language to configure any part of your system
- You can make use of an extensive ecosystem of easily composable, prebuilt NixOS modules
- You can seamlessly integrate with Nix, allowing for ephemeral development environments and shells with packages, eliminating much of the need for imperative package management
- Everything in a Nix-based system must be derived strictly from (lockfiled) inputs, making the reproducibility guarantees incredibly strong (barring any network errors or resources being taken down)
- The declarative nature of anything Nix-based means that every change is documented - your system never shifts from the source of truth, compared to other distros where discipline is required to maintain reproducibility
- Nix is so robust that you could even nuke your filesystems on every log out, if you'd like