Organising Nix on macOS
January 15, 2024
by Jacek Galowicz
I not too long ago purchased a Macbook as a result of an increasing number of individuals are asking me methods to
use Nix in sure conditions underneath MacOS.
On this article, we stroll by putting in Nix on MacOS and see how nice
the expertise is nowadays.
After that, we present methods to go declarative on MacOS with nix-darwin
to allow
compilation for Linux and Intel Macs, in addition to another good options.
🤷 Why would one wish to have Nix on a Mac?
There are a number of causes:
Along with that, we’re taking a look at nix-darwin
on this article, which provides
the next causes on high:
- Declarative configuration of all of your macOS system settings
- Set up of packages and configuration of these
- Seamless integration into
launchd
for configuration of extra daemons - Painless administration of native Linux builder VMs
This text is about two steps and supplies more information for every:
- Set up Nix
- Bootstrap
nix-darwin
On each new Mac, it’s simply two command-line invocations in case your nix-darwin
configuration is already ready.
🌱 Step 1: Putting in Nix on macOS
There are a number of methods to put in Nix.
The presumably most blatant manner (at the least on GNU/Linux) is to make use of one other package deal
supervisor to put in Nix.
Typically, don’t do this.
Particularly on macOS, three other ways work effectively:
- Official Nix installer from
nixos.org
- Determinate System’s Nix Installer for the shell
- Determinate Systems’s graphical installer for macOS
The determinate methods installers have a couple of benefits over the unique Nix
installer:
- It supplies a nicer overview of the set up steps
- It’s designed to outlive macOS upgrades
- Nix Flakes are enabled by default
- The set up and configuration may be personalized
The graphical installer has two extra perks:
I selected to make use of Determinate System’s shell installer, which is a one-liner
as described in
their GitHub repository
(unfold over a number of strains right here to extend readability):
The installation just takes a minute or two.
After running the command, the installer asks for the sudo password and then
prints a nice explanation about what it will do with our system, which we can
accept or deny:
It’s advisable to check if there have been any errors during the installation
and if there are none, close the shell and start a new one.
We don’t need to restart the system.
To test if Nix generally works, just run GNU hello or any other package:
If you’re new to Nix, make sure you get a copy of the
Nix cheat sheet.
It’ll provide the very best overview of the instructions obtainable!
🛫 Step 2: Going declarative with nix-darwin
With Nix put in, now we have all of the Nix shell magic (utilizing nix develop
,
nix shell
) at our disposal and may construct and run (utilizing nix construct
and
nix run
) random initiatives/packages from the web, which is nice.
Nonetheless, if we think about having to put in packages utilizing
nix profile set up ...
on each different Mac, we’re not significantly better off than
with classical package deal administration.
Additionally, this doesn’t handle our configurations and companies, which is precisely
what we’re used to from NixOS.
The final concept is that we wish to have one massive configuration file
(presumably scattered over a number of recordsdata for higher construction and composability)
that units our system up as we would like it with one single command.
That is the place nix-darwin
enters the
scene:
Because the venture description says, this venture goals to convey the comfort of a
declarative system method to macOS.
Ranging from zero, we are able to initialize a brand new nix-darwin
configuration file in
some configuration folder:
This creates a flake.nix
file like this:
{
description = "Example Darwin system flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nix-darwin.url = "github:LnL7/nix-darwin";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = inputs@{ self, nix-darwin, nixpkgs }:
let
configuration = { pkgs, ... }: grep wget
environment.systemPackages =
[ pkgs.vim
];
# Auto upgrade nix package and the daemon service.
services.nix-daemon.enable = true;
# nix.package = pkgs.nix;
# Necessary for using flakes on this system.
nix.settings.experimental-features = "nix-command flakes";
# Create /etc/zshrc that loads the nix-darwin environment.
programs.zsh.enable = true; # default shell on catalina
# programs.fish.enable = true;
# Set Git commit hash for darwin-version.
system.configurationRevision = self.rev or self.dirtyRev or null;
# Used for backwards compatibility, please read the changelog before changing.
# $ darwin-rebuild changelog
system.stateVersion = 4;
# The platform the configuration will be used on.
nixpkgs.hostPlatform = "x86_64-darwin";
;
in
{
# Build darwin flake using:
# $ darwin-rebuild build --flake .#simple
darwinConfigurations."simple" = nix-darwin.lib.darwinSystem {
modules = [ configuration ];
};
# Expose the package set, including overlays, for convenience.
darwinPackages = self.darwinConfigurations."simple".pkgs;
};
}
At the beginning, we generally want to change two things here:
- The
nixpkgs.hostPlatform
setting must beaarch64-darwin
on Macs with
Apple Silicon CPUs. On Intel-based Macs it can be left asx86_64-darwin
. - The
simple
part at the bottom of the file in the
darwinConfigurations."simple"
attribute can be renamed to our hostname.
This way we don’t need to provide the name explicitly when building or
rebuilding the system configuration.
This is enough to start.
Bootstrapping this new configuration can be done even without installing any
nix-darwin
-related packages with a single command:
The last parameter still needs to be
--flake .#simple
if we didn’t rename
the configuration attribute at the bottom of the file.
The installation process might warn us of files that could destructively be
overwritten.
We need to back up or remove them (on a new Mac, I typically just delete)
first:
After handling these files, we can simply run the script again.
nix-darwin
is now bootstrapped on our system, which gives us the
darwin-rebuild
command that is similar to nixos-rebuild
on NixOS hosts.
We can now run darwin-rebuild switch --flake .
anytime.
Additional nix-darwin
goodies
The new nix-darwin
config did not do much to our system.
It’s just a starting point.
What now?
Have a look at the
nix-darwin
configuration options Documentation
which lists and describes all of the obtainable choices.
This overview is a goldmine – there’s something for everybody.
Let’s take a look at a couple of good examples:
Unlocking sudo
by way of fingerprint
If now we have a Mac with Touch ID, we are able to
unlock sudo
instructions with our fingerprint as an alternative of typing the password.
That is after all not unique to nix-darwin
customers, however these have it
notably simple to allow it.
Merely add the next line to the brand new config:
Rebuild and apply the config using
Generally, reboots aren’t necessary, but this specific setting needs a reboot.
Voila, this is how it looks in action:
Setting System Defaults
nix-darwin
provides configuration lines for many different macOS default
settings.
These can typically be altered using UI application setting dialogues or
with the defaults
terminal command.
However, nix-darwin
manages them all for us:
system.defaults = {
dock.autohide = true;
dock.mru-spaces = false;
finder.AppleShowAllExtensions = true;
finder.FXPreferredViewStyle = "clmv";
loginwindow.LoginwindowText = "nixcademy.com";
screencapture.location = "~/Pictures/screenshots";
screensaver.askForPasswordDelay = 10;
};
This example configuration snippet sets:
- macOS dock hides automatically
- Don’t rearrange spaces based on the most recent use
- Finder shows all file extensions
- Default Finder folder view is the columns view
- The login window shows a specific text as a greeting
- When taking screenshots, store these in a specific folder
- Only ask for a password in the screensaver if it is running for longer than
10 seconds
Apple Silicon Macs: Compile Intel Binaries
Apple Silicon Macs can install
Rosetta, which allows the
system to run binaries for Intel CPUs transparently.
The set up nonetheless must be completed manually within the terminal with this
command:
After that, we can add this line to our nix-darwin
configuration and rebuild
again:
Now, we can build and run binaries for both CPUs:
$ nix run nixpkgs#legacyPackages.aarch64-darwin.hello
Hello, world!
$ nix run nixpkgs#legacyPackages.x86_64-darwin.hello
Hello, world!
Although this feature is a relatively specific developer use case, it’s nice to
see how easy it is to configure.
Building Linux binaries
If we want to build binaries or even full system images for GNU/Linux systems,
we typically end up delegating builds to remote builders.
nix-darwin
provides a neat Linux builder that runs a NixOS VM as a service in
the background.
It can simply be activated with one additional configuration line:
It works on both Apple Silicon and Intel-based Macs.
The VM itself is bootstrapped by downloading it from the official NixOS cache.
It comes with pre-installed SSH keys, which nix-darwin
also handles elegantly
for us on the host side.
After rebuilding the system, we can test it.
With a quick dummy derivation that simply writes the output of the command
uname -a
into its output path, we can check that it is executed in fact on
our new Linux builder:
$ nix build
--impure
--expr '(with import <nixpkgs> { system = "aarch64-linux"; }; runCommand "foo" {} "uname -a > $out")'
$ cat result
Linux localhost 6.1.72 #1-NixOS SMP Wed Jan 10 16:10:37 UTC 2024 aarch64 GNU/Linux
Wow – how does it work?
- There is a daemon called
org.nixos.linux-builder
running on our system,
which keeps SSH keys and disk image in/var/lib/darwin-builder
/etc/ssh/ssh_config.d/100-linux-builder.conf
creates an SSH host-alias
linux-builder
/etc/nix/machines
contains a remote builder entry
This specific VM is also documented in the
nixpkgs documentation.
📲 Updating the System
Updating the system includes two steps:
- Updating the Nix flake inputs
- Rebuilding the system
If the configuration resides in a git repository,
nix flake update --commit-lock-file
can automatically commit the lock file
changes.
Conclusion
Some Apple fans might like setting up a new system each time, but most of us
want things to be simple and in sync.
Nix in combination with nix-darwin
is an unbeatable combination – on a new
Mac, we can simply perform two steps:
- Install Nix with one of the installers
- Run
nix run nix-darwin -- --flake github:my-user/my-repo#my-config
…and we’re done.
From Finder etc. UI settings, over preinstalled packages, to additional daemons,
it’s all in there!
If you have questions on how Nix fits into your corporate workflow or how to
make it work for you to make your teams even more productive, don’t hesitate to
reach out to us via mail or schedule a
quick call ! 💪