Hacker News new | past | comments | ask | show | jobs | submit login
Going declarative on macOS with Nix and Nix-Darwin (nixcademy.com)
91 points by jonge 8 months ago | hide | past | favorite | 61 comments



I've used Nix with Nix-Darwin on my macOS machines for a couple years now. The primary benefit is that when I get a new macOS machine, it's only three steps to having ALL my apps, configurations, etc. exactly as they were before:

1. Download Nix installer (I prefer the one by Determinate Systems)

2. Clone my Nix configs (public, so I don't even need secrets like keys yet)

3. Run `make`

I then wait around 15 minutes or so (mostly network time) and I'm good to go.

ONE BIG RECOMMENDATION: I don't like using nix-darwin to manage graphical apps directly. It's a bit awkward. But, you can use nix-darwin to declaratively manage `homebrew`, so I still get my graphical apps from Homebrew. The linked article seems to suggest ditching homebrew entirely but I found its best as a mix of both worlds with Nix being the source of truth to manage everything else, including brew.

You can find my configurations on GitHub but note that if you're new to Nix you would do better finding a simpler starting point: https://github.com/mitchellh/nixos-config (these are shared Nix configurations between macOS, Linux, and WSL on Windows).


I've just migrated from a 4 year old Macbook to top of the line M3 Max. My previous Macbook had multiple kernel extensions, lots of customisation and homebrew apps and such. I was convinced that I'd spend a significant amount of time setting it up again - so I'd made some bash scripts in preperation to reinstall my homebrew stuff etc.

So upon receiving my macbook I used the migration assistant and ticked everything, and to my surprised I've spent less than an hour getting this new machine up to speed. It moved everything including all my settings, apps, configurations for those apps etc. The only issues I've had is docker didn't move across, and I've had to manually download universal binaries of a few intel apps. It has been completely flawless.

I don't see the value add for the use-case you describe of machine migrations when Apple's tool for this works so well.


I agree, I'm not getting new computers more than once a year, at most. Setup time doesn't seem like something I should optimize for.


I would argue the benefit is also it’s declarative, done forever, and your machine becomes relatively bulletproof.

Dev environment issues are a thing of the past, once you’ve defined your configuration.

If something is broken with a package, I don’t have to figure it out myself —- I just rollback, wait for someone to fix it upstream in nixpkgs and pull down the patch later.


At least in my opinion, the leverage here isn't about directly saving time in some hypothetical universe where you set up new devices every week.

It's about confidence in your ability to quickly bootstrap a productive system and the relative freedom/security that flow from knowing it.

When you know you can be productive this quickly without access to a backup or a working device, you have relative freedom and security from a decent spectrum of manufacturing defects, hardware failures, disasters, accidents, thieves, and so on.


> The primary benefit is that when I get a new macOS machine, it's only three steps to having ALL my apps, configurations, etc. exactly as they were before

This is the same for macOS? I’ve done that twice in the last couple of years and it was just a matter of letting the migration assistant run and letting Homebrew install the list of packages exported from the old system.

This is not to say anything negative about Nix, only that this particular point doesn’t seem like a big selling point for something I do every few years.


For single-user single-device scenarios, you probably won't find Nix much better than brew or apt. Declarative infrastructure isn't very necessary when you're the only person running your software. With multiple devices, you at least want a way to automate your build process. Bash scripts and Makefiles work fine, but they both fail silently and won't work consistently across devices, OSes or even architectures.

Nix smooths that out, which is great for a single user with multiple devices but even better on teams. Instead of coordinating devices individually, you can update Nix environments and push them out to everyone at once. The build environment is self-contained, isolated and updated silently alongside the rest of your repo. On larger teams, that saves a lot of configuration headache.


> these are shared Nix configurations between macOS, Linux, and WSL on Windows

This is the "killer app" of Nix for some people. I've got a lot of machines, spread out between a variety of usecases. With Nix, I can have individual module files for my desktop apps, my gaming software and my terminal configuration, then link them into the various machines that need it. Once it's fully set up, you can essentially manage the environment of dozens of different machines in a single Git repo.

Nix will frustrate people because it doesn't offer a lot of imperative or immediate solutions to things. If you can handle the learning curve though, you'll be hard-pressed to find a more comprehensive package management solution on any OS.


Despite how the learning curve keeps the Nix community relatively small (it's grown a lot since 2021 actually) nixpkgs has assimilated like 90% of OSS. Learning Nix is hard, but once you know enough Nix to be productive it's a huge enabler.


How does Nix manage Homebrew? Haven’t heard of that. Does it make Homebrew declarative somehow?


Unclear at what level you're asking, but Mitchell's comment identified it as a nix-darwin feature, and you can see how he's using it in the config he linked at https://github.com/mitchellh/nixos-config/blob/b73a16fc918c5...


nix-darwin supports managing your Brewfile.

The docs[2] are very helpful!

[1]: https://github.com/Homebrew/homebrew-bundle

[2]: https://daiderd.com/nix-darwin/manual/index.html#opt-homebre...



What's the benefit vs a restore from backup step which also restores your data in addition to apps and configs (and avoids any network)?


In exchange for having taken the time to understand and declare exactly what you depend on to be productive (and nothing else), you get to start ~productively-fresh instead of living in a cargo cult of whatever unexamined arbitrary state and executables were present at your last backup.

It also means you aren't completely out of luck if you get lazy and don't test your backups for 6 months only to find out they stopped working after you ran some `blah blah update/upgrade` command.


This isn't veggies, so what's the actual benefit of freshness?

Also not clear on the backup risk - why would a disk snapshot stop working because you've updated with blah?


I guess having your config as a text file in a public repo instead of a multi GB private file can be nice.


> It’s better than homebrew

It feels like every Nix post that gets on Hacker News has a version of that claim, yet it’s seldom expanded on. In this article, the author throws that in with zero explanation and never revisits it. In other words, it’s opinion and not fact, which is something I do not want conflated in a technical post.

I wish these unproductive software animosities would cease. I don’t care if you use Vim, Emacs, Helix, Windows, macOS, Linux, iOS, Android, or Symbian. Use whatever works best for you and let everyone else do the same. If you have a specific point to make about why a piece of software should be avoided¹, make your argument and let people judge it, otherwise let it go. Why do you feel the need to throw shade on anyone else’s work, especially open-source software? We’re supposed to be a community, not be backstabbing each other.

We’re talking about package managers, here. It’s a bloody tool and you’re treating it like the worst parts of sports fandom.

¹ E.g.: You may feel strongly that Chromium browsers should be avoided because they give Google more power and that’s a risk to the open web which itself becomes a detriment to society.


I don't mind the opinion "it's better than homebrew"

The thing is, everything is tradeoffs. In some ways I'm sure Nix is better than homebrew.

If you want to compel me to try it, tell me why you think it's better than homebrew so I can understand if it solves problems that I also have.

That's what bothers me, I know not everyone uses my stack and I do love hearing about what others are doing differently but please please please tell me why you think it's better.

Simply saying "Vim is better than standard editors" is pointless, saying "Vim keyboard shortcuts have an extremely steep learning curve, but, once the new way of thinking becomes second nature to you, you will find that you can do things far faster than before. This is especially useful for me personally as the 'context switching' from mouse->keyboard->mouse->keyboard->mouse was an extremely limiting factor for how quickly I can work and Vim (in particular Vim keybindings -- I use them in VSCode today) allows me to keep my fingers on the keyboard for more time before I do something which /requires/ the mouse, this makes me more productive. That said, the only way to learn that was to force myself to use Vim and no other editors which resulted in what was probably a 2-3 week period where I was completely useless, but, ultimately 15 years later I'm sure I've made that time back tenfold."


> In other words, it’s opinion and not fact, which is something I do not want conflated in a technical post.

As you’ve already stated, it’s an opinion and not a fact. I wouldn’t take posts like this so personally. Instead, use content like this to help you explore new tools. If you like it, great! If not, move on. Simple as that.

If you’re new here, people can be very passionate about their tools.


> people can be very passionate about their tools.

I’m aware. It’s not a coincidence that I started my examples with Vim and Emacs, the classic flamewar. There’s nothing wrong with being passionate for something, that’s not the argument. My point is that just because you like something you don’t have to be against an alternative. Nix and Homebrew, Vim and Emacs, these are parallel tools which can coexist, not rivals in a reality show.

> I wouldn’t take posts like this so personally. Instead, use content like this to help you explore new tools. If you like it, great! If not, move on. Simple as that.

There’s nothing to take personally, and I do explore tools which interest me.

I feel like you haven’t read or understood my point at all. You’re ostensibly disagreeing while making the same point. I’m advocating precisely for the unwarranted negativity to stop. If you want to talk about why you love something, do, there’s no need to send subtle jabs at something else other people are working on and sharing for free. That’s incredibly demotivating for developers and leads to burnout, for nothing.


People can also be very objective, though. This is also HN.

You can BS enough, but then you have to take some s** back if someone calls you out for making such statements :)


Yeah, I've tried both nix and brew, and `brew install` is just much more convenient than editing a config file and then running `home-manager switch --flake .`


Homebrew doesn't have to be run imperatively — there is brew bundle, which keeps your installations in sync with a file. It's not nearly as complete as nix, but if all you aim to do is manage your packages through a single text file instead of searching your shell history for `brew [un]install`, it works great.


Not sure what the situation on MacOs is, but you don't need to use home-manager, do you?

You should be able to simple use:

nix-env -I <package_name>

Or (new flake enabled CLI tools):

nix profile nixpkgs#<package_name>

That's quite close to other package managers. You still have the massive nixpkgs repository and tools like nix-shell as an advantage.


Or frequently just `nix shell nixpkgs#foo` or even `nix run nixpkgs#bar -- --baz` (as opposed to `brew install foo; foo; brew uninstall (or it is it remove?) foo`

I still have both installed as the union of their packages is better than either in isolation, but I really like having a good idea of what I have permanently installed (and why) in my nix config files, as opposed to re-spending 5 minutes in the brew docs to remember the brew incantation that will give me a reverse dependency tree and try to sort out whether I can uninstall something without breaking something else.


Why would you need to remember dep tree if brew will simply fail to uninstall an app if something else depends on it?

The other solution to this and the other is shell aliasing, make "package manager rm" uninstall regardless of the package manager, and you don't need to remember


I often end up scripting something with the dependency tree as opposed to recursively trying to uninstall dependencies that I don't recognize until I get to something top-level that I recognize.


Someone heavily downvoted your comment - not sure about why.

The author should have taken the time to state that homebrew doesn't offer features such as ... "environments" or things that help with build reproducibility. A bit unfair, though, because that was probably never Homebrew's mission.


> The author should have taken the time to state that homebrew doesn't offer features such as (…). A bit unfair, though, because that was probably never Homebrew's mission.

Depends on the wording. It’s perfectly fair to say “I use Nix instead of Homebrew because the former supports X which is useful to do Y”. That way it’s not important if Homebrew intends to have Y or not, it just clarifies what the author cares about in a package manager and the reader is better informed if they care about the same things.

Or they could’ve not mentioned Homebrew at all. There’s zero positives to that sentence in that post.


Nix seems like something I should absolutely be using, but it also makes me feel like a complete idiot. The documentation, the terminology, and even tutorials like this one feel so… arbitrary?! and leave me more confused than I started. Look at that flake file. It’s daunting! Maybe I’m just getting old.


Here’s Mitchell Hashimoto (founder of HashiCorp) setup config.

https://github.com/mitchellh/nixos-config


Very interesting. I’ll have to give this a shot


I am the only one who finds it highly annoying that everyone assumes Nix should install itself in the global shell configs?


I'm sure you're not the only one, but I'm perfectly fine with it.


That, and also that it has to be installed globally with the absolute path of `/nix/`. Sure, you can change it, but the packages won't work then.


That’s been an issue for ever. I can only assume it’s a really hard problem to make the install location configurable. Plus, folks like me who don’t use Mac really don’t care. It’s a perfectly fine place to it.

EDIT: LatticeAnimal answered it. You can’t use the cache with a custom directory.

And it has to be global because the whole point is that there’s only one place and all derivations are shared between user accounts. If everyone had a ~/.nix, hard drives would explode on multi-user systems.


A better place would be /opt/nix, that’s what /opt is for¹. There is a Filesystem Hierarchy Standard² and following it would lead to fewer incompatibilities than ignoring it.

¹ https://www.pathname.com/fhs/pub/fhs-2.3.html#OPTADDONAPPLIC...

² https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard


NixOS and nix, by design, do not and can not follow FHS, so I guess they decided to shorten that path while at it. That said, it is possible to use a different location than /nix/store through chroot/proot/bubblewrap or similar mechanisms and AFAIK the `nix` CLI can do this natively.


Perhaps one of us is misunderstanding the other, but I don’t mean follow FHS for what it installs, I mean doing it only for its root. /opt is exactly the place where you put the “other”. Homebrew (by default on Apple Silicon) does it perfectly well in a self-contained manner that does not need to fudge with the hierarchy.


I understand what you mean, I just made a guess why it was not a priority to follow FHS for its root when it doesn't really follow FHS anywhere else anyway. Also, /nix is self-contained as well, so that is not an advantage to following FHS.


Moving /nix to another location would require a lot of package authors that make the assumption of /nix/store being a constant to rewrite their packages, an effort that doesn’t really seem worth it.


I don't believe this is a big issue, typically package authors just use $output variable which returns whatever path the package should be stored at.

The real reason for it are libraries. The binaries that are packaged in /nix/store very often depend on at least glibc and many other libraries. Their path is hardcoded to /nix/store/...

Changing the path would require recompiling, which also means all the caches that were generated would have to be purged and everything would need to be rebuilt.

Though in my limited experience I was able to also use a symlink. I got a warning that some thing could break but all things I was interested in continued to work.


One of the points of Nix it to _not_ adhere to FHS


See my reply to a sibling comment making the same point: https://news.ycombinator.com/edit?id=39005803


iirc, you can have a different base path, but you have to recompile every package locally to have the correct patched paths. I forget where I read about this.

Seems like an unavoidable consequence of how modern software is built


It's actually an unavoidable consequence of what makes Nix great. There is no loader that looks up libs in /lib, the paths are directly hardcoded into the binary. This is how Nix sidesteps versioning hell entirely.


It really isn't unavoidable on macOS. @rpath is designed specifically to handle this sort of thing, and is how all the command line tools distributed with Xcode link to libraries embedded in Xcode and continue working even when you drag Xcode to a new location.

Admittedly supporting that would require updating how all the tools are built and not just defaulting to Whatever Linux Does™, which is probably too much effort to justify in this case, but it is hardly an unsolvable (or even an unsolved) problem.


As far as I understand, you could pad the paths for the server farm builds then overwrite them on local machine install like Spack package manager does, so maybe not unavoidable?


The store path can be changed with chroot or something similar and the `nix` CLI supports this natively, AFAIK.


That's more reasonable; if you want it stored elsewhere you can always use a bind mount.


It breaks things on Ubuntu LTS (which I am forced to use on my work laptop if I want to use Linux). Somehow glibc versioning gets bjorked.


My nix-darwin/home-manager setup -> https://github.com/amodkala/dotfiles


Last year while working in a nix using team, Golang was lagging behind on nix by a few months, I wanted to use a new feature from the latest version and it lead to some awkward conversations. Nix is not a drop in replacement for homebrew, nor is it fast when it comes to community updates. If you have a small number of packages it works well, but if you only use a few number of packages, why do you need a heavy package manager.


I'm also using NixOS and working on Go projects, and had to deal with out-of-date Go releases. Nixpkgs generally does get the latest Go versions pretty quickly, but only in the unstable channels, they're not backported to NixOS releases. You can just grab that one package out of nixpkgs-unstable or nixos-unstable, like:

    (import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz") {}).go_1_21
where you had `pkgs.go` before (in your shell.nix or wherever).


> Nix is not a drop in replacement for homebrew, nor is it fast when it comes to community updates.

I am certain there are some exceptions and edge cases to this, but in my experience nixpkgs is the largest and most up-to-date repository of all, by a wide margin. Did you use nixpkgs-unstable or a stable release?


While I am a happy user of Nix and think that the unstable channel is generally recent enough, I have not quite found it to be the "most up-to-date". Among the package repositories I have used, that honor probably goes to openSUSE Tumbleweed.


Use Nix on a Mac they say, it’s easy. Just follow this 20 page long blog post and then spend every day fighting against how the OS wants to work.

I’ve tried Nix on a Mac half a dozen times. It always ends with frustration.

Homebrew works amazingly if you actually want to be on a Mac. If you HATE everything about MacOS and Apple and someone forced you to use a Mac, then use Docker and Nix inside of that.

Nix should never be used on a Mac. It is a philosophical disaster. Don’t do it. Delete the repos and the world will be better off.


I’ve been using Nix directly on MacOS with minimal issues for a few years now. Works great.

I hate using docker on Mac and have since replaced it with flakes and devenv.

My configuration with a step by step guide (600+ stars): https://github.com/dustinlyons/nixos-config


I used your repo as a foundation for my setup. it was a big help getting started. Many thanks!


> Nix should never be used on a Mac. It is a philosophical disaster. Don’t do it. Delete the repos and the world will be better off.

It's fine if you don't like Nix on Mac. It's fine to not use it and to voice your concerns. But calling it a net negative for the world is uncalled for and in my opinion extremely disrespectful to the maintainers doing an enormous amount of work for free.

On another note: I am extremely happy with my Nix on macOS setup.




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

Search: