When i install a fresh macos i have two commands to run - install nix using the determinate systems installer, then apply my nix config.
It's not quite as streamlined as nixos but good enough.
My biggest remaining pain point is dev envs - i've been leaning into adding a flake in each project, so for example i have a single project that's written in scala 2.13, when i cd into that project dir, the correct jvm version, sbt, intellij etc are installed, some useful env vars and shell aliases etc. - that's all great (i haven't felt the need to adopt denenv.sh or flox yet) but i do find myself wanting a devcontainer sandbox workflow more often these days (blame cli coding "agents"), i lean on vscode for that rather than nix so far. In python (where i spend a lot more time) uv loses a lot of value in nix and i don't like that.
Comparable to the amount of breakage I regularly had with homebrew.
> The consequence is me, spending a few hours debugging my environment instead of writing code.
But then I also see this:
> I’ve spent a lot of time recently moving my entire workflow into a declarative system using nix.
I can see how this can be beneficial for someone who switches systems very often, reinstalls their OS from scratch very often, or just derives a lot of pleasure/peace of mind knowing that their dev env is immutable.
I change computers once every 6 years or so, maybe more. To me this looks like exchanging a couple (hypothetical) hours of debugging 6 years in the future by tens of (guaranteed) hours trying to climb up the nix learning cliff.
I am happy that it works for the author though, and knowing that it's possible is good in case my particular development circumstances change.
It's also about peace of mind like you said. Before nix I sometimes felt anxiety installing or upgrading certain things on my computer. "Will this upgrade break stuff?" - and often it did and I'd have to spend the next few hours debugging. With nix I don't worry about any of that anymore.
Even for things like trying out a new shell you can temporarily move the dotfiles somewhere and restore them back and it still takes less time than converting everything to Nix.
If you just want a throwaway VM, it's straightforward to create one through the UI cloud console. Whereas, terraform is nevertheless still a useful tool to use to manage VMs.
For stuff like installing development dependencies.. it's maybe not difficult to copy-and-paste instructions from a readme, but solutions like devcontainers or Nix's development shells can be useful even if costing more overhead.
My biggest complaint is what I mentioned above: it’s trying to be everything for package management, and adds a lot of complexity (and I disagree that it’s always necessary/inherent) compared to just installing a tool and sometimes upgrading it. That complexity often means I have to debug it rather than the tool that I want to - I might have to debug Nix instead of Node, which is not always straightforward. In my limited experience Nix got in my way more than I’d like, and in ways I didn’t expect or want to deal with, and until it’s as seamless as something like Homebrew or apt, it’ll be a hard sell.
Although you're right about nix's DX being quite rough, the problem isn't exactly that it "tries to be everything for package management".
Consider the assumption Nix wants to make about its packages: it should be possible to package software by putting it in some arbitrary directory (i.e. not just /usr/bin), where its dependencies are also put in (or symlinked to) some arbitrary location.
I think with well-written software, this should be a reasonable assumption, but you're going to run into friction with that. (Friction which will require you to have a good breadth/depth of understanding).
In my experience, a lot of the complexity when dealing with Nix is with the large and organic complexity of nixpkgs.
The "trying to be everything" is more incidental to the expressive package management. -- NixOS is 'just' an OS built upon the package manager; dev shells are 'just' shells which expose the build environment of a package; etc.
I think the closest mainstream UX for "you can try out this program without having to install it" is running a Docker image.
:) I'd say Nix is second best at everything related to packages.
One thing I haven't tried yet is building a container from a flake, which would have obvious benefits for reproducibility. Still don't think it would help with service orchestration though.
A simple UNIX script or PowerShell utility takes care of it.
None of the ones I have used during the last decades has ever grown to more than like 20 lines of code, minus comments.
Couldn't pretty much all of that be addressed using containers? Keeping your base system clean does sound wonderful, but eg distrobox containers sound more approachable - you're using all the same commands that you normally would, apps are in an environment much closer to what they probably expect. You can still roll back using snapshots, which you can configure to be automatically created on system updates. If you want an atomic rollback guarantee, and a strong reminder not to mess with the base system, you can use an immutable distro (talking about Linux, not macOS here). The one big advantage that I see from nix is reproducibility. But it's not clear how desirable that is for a desktop use case. You may actually want different software on different machines. Having an overview of all the changes you made to your system sounds cool, but I'm not sure it's worth the effort that comes with nix. I'm worried that after 8 months I'll decide it's too much hassle, like many commenters seem to do, and end up switching to a simpler system with dotfiles and containers, wishing I'd done that from the start.
yes, it sounds like it's not worth it for you -- you will have to spend a significant amount of time converting your system to do things "the nix way". you can try to do this incrementally, but it's a time sink, and really easy to get stuck bikeshedding instead of doing work.
for me, it feels like a near equal trade-off between debugging nix, or debugging some random env issues that pop up. i know nix, claude code "knows" nix, a lot of other people online know nix. random env issues are random, and yield worse results on google, and frankly are much more frustrating to the point i would rather spend more time with nix than deal with them. maybe a very weird view.
The only way you get positive ROI from Nix is either you enjoy the journey, or you use it to do more than just managing a single computer: you manage a fleet, you build thin application container images, you bundle all your software, you have devshells, repeatable tests and deploys, etc. It's the same tool for all of them.
Nix is a wonderful technology. But I would not argue it is practical if you can afford "just fix it when it breaks". A nix setup more/less requires you to pay all the cost up front.
I appreciate putting in the effort now so that I don't have to later for stuff like declarative dev environments. It's really nice to not have to copy-and-paste installation instructions from a README. -- I did like the point: until you've felt what a comfortable design is, you cannot imagine it.
At least from my usage, the fact that your configuration is all tied to whether the entirety of nixpkgs-unstable is working can be a real headache. Like recently when CMake was upgraded to 4.0, it took down a healthy handful of packages, which meant you couldn't update anything until things were resolved or you were really fluent in Nix hackery. (I was the former)
1. Add a new input to your flake of nixpkgs pinned to a specific commit or branch. For example:
nixpkgs-91fab2d.url = "github:NixOS/nixpkgs/91fab2d3e8ee06464f3c9896e84c97982b771fc3";
2. Add an overlay where you replace those packages with versions from the older nixpkgs. For example: (final: prev: {
inherit (pkgs-91fab2d) firefox libreoffice;
})For me Nix/NixOS is by far the most effective for deploying servers, development VMs, etc. I do this regularly, so it pays off - I have a familiar environment completely set up in minutes. Another place it pays off, even on macOS, is for development environments, especially if you have to pull in a lot of native dependencies (which can happen in mixed Python + C++ projects, mixed Rust + C/C++ projects, etc.).
Nix shines in all difficult cases, like setting up complex cross-compilation environments, building for old glibc versions, etc. You set it up once and then it's really easy for yourself and other project contributors to get the same build environment.
Like any tool, a good engineer knows when to apply it.
Er, YMMV I guess? IMO if you're spending more than an hour fussing with it per 6 months, you're doing something weird
I also really wanted to like the declarative homebrew configuration but it also often didn’t work as expected for some configurations and had a lot of leaky abstractions that straight up just broke sometimes.
If I ever go back to managing my Mac with nix I would probably just do a home-manager setup and just install most of the applications imperatively.
Given this was using an intel based machine around the time when the switch to arm came so a lot of breakage also stemmed from that.
I still use nix to handle my homelab.
My setup up on my Mac is as follows:
- Orbstack
- NixOS machine run in orbstack
- My whole dev environment is run from this container and is very transportable
- GUI apps are installed on my Mac using the App Store or homebrew etc. but I try to reduce the amount of installed applications
- if I have to install something that I don’t want to install but have to, I try to do it in a UTM machine.
$HOME/bin
along with extending the $PATH. Works great for most of my tools (exa, zoxide, bat, jq, etc).I still believe something like nix is the future of building software, I'm just not sure it'll be nix itself.
Nix may not be the tech that replaces everything, but at the very least it is and has been an important exploration vehicle for declarative configuration, immutable systems, etc.
This is just procrastination.
I think where Nix shines isn’t “one laptop every 6 years” but when your environment needs to be shared or recreated: multiple machines, a team, or a project with nasty native deps. At that point, nix-darwin + dev shells becomes infrastructure, not a hobby. You don’t have to go all-in on “my whole Mac is Nix” either: keep GUI apps and casual tools imperative, and treat Nix as the source of truth for the stuff that actually blocks you from doing work. That hybrid model matches what the article hints at and tends to give you most of the upside without turning your personal laptop into a second job.
If I make a git repo, place '~/.config/newsapp.conf' in there and then symlink it back to '~/.config/', if NewsApp introduces a new variable in its settings I am immediately aware because Git will complain about being dirty. However, Nix will happily just nuke that .conf and rebuild whatever is in your configuration, without letting you know about state. Which is ultimately bad for reproducibility. It's a huge blind spot in Nix.
All of it seemed way too annoying compared to just having a dotfiles repo, and if it couldn’t handle the Tower/gitconfig issue I know for sure everything else I was going to run into wasn’t worth it.
The way I do it is have config files in my Nix config folder, then use Nix to symlink them and I use git to make me aware of state changes, that I might want to make reproducible. But that's just me being used to my old git ways, using 'nh' gives much more clarity.
The "true" Nix way is putting the entire contents of whatever config file in a .nix file, then erasing the original config and have Nix recreate the config (preferably read-only) in place. You become truly reproducible but for obvious reasons applications get mad when you make their config file read only.
If you use Nix you're completely blind to that unless you A) religiously check your state or B) use another tool like 'git' or 'nh'. Like I said, it's a blind spot, both figuratively and literally.
If you try to symlink into an existing file Nix will indeed complain, which is why you have to erase the config file and then put yours in place. You can either write it out from a .nix file (pure reproducibility) or from symlink copy from a file managed by git (technically impure but effectively reproducible).
This is not only a Nix problem, fwiw. I first encountered this with, say, running ZNC in Docker. It will happily write logs and configs to a temporary directory in the container and get blown away on restart unless you mount persistent volumes in just the right spots. Syncthing can be configured to manage its own config or it can be configured fully by Nix, etc.
I'd like to add the third thing, which is just iteration. It's very tricky to maintain advanced workflows even locally. I'd guess many won't even try to compose things that could work in combination (often self-hosted services), when they know they can't reliably maintain those environments.
I landed somewhere similar for gaming.
When I first got my Legion Go, there were a whole bunch of gaming distros, but none was more popular than the other. I read a bit about immutability, got curious, and installed Jovian-NixOS (which is to NixOS what Bazzite is to Fedora).
I liked that it gave me the Steam Deck experience on unofficial hardware, and it was neat to be able to play with e.g. replacing the desktop shell. However, a keyboard-centric configuration scheme is a bad pairing for a touch-centric device. Little things, like changing the timezone, became needlessly frictionful.
Now I'm on the official SteamOS, with Nix as the package manager. It seems to be a good pairing. Nix lets me install Linux utilities on Steam's otherwise-immutable filesystem. And I can use the Steam UI to change things like the time.
I'll probably write an article in the next few weeks on using Nix to make Linux-native copies of Windows-built games.
But I think just in fairness, the comparison here for flakes should be to Homebrew bundles. My packages are managed in a bundle: https://github.com/Julian/dotfiles/blob/main/Brewfile and then locked by a lockfile: https://github.com/Julian/dotfiles/blob/main/Brewfile.lock.j... and installing is just `brew bundle install`. All native Homebrew functionality. In practice I have never had an issue with non-reproducible builds across my machines (partly because the tendency on macOS is to run the latest versions of things and stay up to date).
(But again I do find nix-darwin interesting to try for other reasons.)
The bundler integration for nix-darwin actually just bakes tightly-controlled Brewfiles. It’s still worthwhile though, since part of the “tightly-controlled” means better cleanup when you remove things.
(I'll still stick with "I never really have run into a version issue for things I use Homebrew for, for places where it matters, I have whatever-programming-language-lockfile-for-the-project-I-am-developing" for cases where I need to be sure the setup is reproducible, which is why I've clearly never noticed this file was useless).
I would recommend it only if this type of thing naturally interests you. I can't imagine powering through the initial learning curve if it felt like a frustrating chore.
That said, if having (most of) your machine defined declaratively in a git repository sounds exciting/useful/comfy, then I would encourage you to give it a try. You can start small by just configuring a few programs or options and see how you like it.
I wrote more about my experience here where I also link to my configs: https://bryce.is/writing/code/fully-nix-pilled
Why do you want to get rid of something which you are not forced to use?
A few of my favorite parts, which I see less represented in this thread so far:
- I simultaneously jumped into nixos on several Linux machines (starting with a few Pis for experimentation), and maintaining all of my systems with a single flake and mostly shared code is a dream come true. - no more convoluted dotfile syncing, most of my scripts and aliases and bash config and binaries all sync together - cross-building linux from Darwin -- including integration tests in a vm -- works surprisingly well, this is mostly just nix but nix-darwin has helpers that make this easier - writing system services (launchd) on my Mac then converting them to a headless Linux machine (systemd) is generally very straightforward - prefixing my path with GNU coreutils works well and saves me from many e.g. `sed` quirks, I get expected behavior across OSes - this was always a sore spot in homebrew, either dealing with the `g` prefix on everything (eg `gsed`) or dealing with intermittent breakages when stuff depends on the BSD behavior - I was also able to put nix-darwin on my wife's MacBook and greatly simplify admin / maintenance tasks I do for her - finally, the nix crew is just thirsty for help and contributions, particularly the darwin crowd; I feel like my (minor, occasional) contributions are celebrated, differences of opinion are met with an open mind, it is in general a far departure from the relative hostility of homebrew - on the down side, I have spent far more of my limited time helping contribute to nixpkgs / nix-darwin / home-manager
I’m enjoying a Fedora atomic distribution for the eliminating the Linux upgrading issues of the past. But macOS and Windows never really had that problem.
There's a lot of reasons to use Nix instead of or WITH Homebrew depending on your exact needs.
Where it’s paid off for me (and where I think it actually wins) is when the problem is recreating environments: multiple machines, teammates, CI, nasty native deps, CUDA stacks, etc. At that point you’re choosing where entropy lives: in invisible drift (brew/manual installs) or in a repo you can diff/rollback.
Also, you don’t always need to go full “immutable everything.” Really depends on your needs here. Hybrid tends to be another sane path. In certain situations this can get you 80% of the upside without having to rip it all out. So kinda the "good enough" which I've seen a lot of folks do.
We (Flox) actually worked on this with Kelsey Hightower a while back - https://bsky.app/profile/kelseyhightower.com/post/3ld2rsccls...
I stick to nixpkgs for most of the foundational system tools, lsps, build tools - basically anything that works in the terminal and is fully open source. For everything else (zoom, slack, vscode, firefox, etc) I use homebrew packages. The combo gives you options and is a strict superset of what you can do with homebrew or nixpkgs alone. Best of both worlds really.
The article I used to help set up, in case this helps anyone: https://davi.sh/blog/2024/01/nix-darwin/
As a bonus I was able to achieve some semblance of uniformity across Mac and my Linux desktop with home-manager.
turtleyacht•1mo ago
Etheryte•1mo ago
Cu3PO42•1mo ago
FireInsight•1mo ago
There's of course Fedora Silverblue / Fedora Bootc with https://universal-blue.org/ and https://blue-build.org/ being good examples.
Recent developments have seen the creation of bootc images for non-Fedora distros too, and at this point I've seen quite a few cool arch-bootc custom images, completely customized to the author's desires. See: https://github.com/bootcrew/, https://github.com/tartaria-dev/tartaria
plagiarist•1mo ago
retatop•1mo ago
undeveloper•1mo ago
Jhsto•1mo ago
That being said, we used NixOS images to boot several Windows PCs of my friends into RAM to play Halo 3 multiplayer split-screen. Most of my friends were mainly confused why they could play with any gamepad they had in their shelf. They also left the event with no permanent changes to their PCs.