With [nix-darwin](https://github.com/LnL7/nix-darwin) and
[home-manager](https://github.com/nix-community/home-manager) it is possible to
manage almost all of a mac configuration declaratively. So when I got my new
Macbook I was pretty sure this is the way to go. Unfortunately, the bootstrap
is a bit involved. These are my notes from the process, which hopefully
serves as a tutorial.
## Installing dependencies
We need to install some software manually before we can go full steam with
configurations stored as nix files, the primary one being `nix` itself.
Install nix with the [determinate systems nix installer](https://install.determinate.systems/), it comes with sensible defaults and a nicer uninstaller.
Unfortunately, the GUI installer installs `x86_64` version of nix, so I had to use `curl |sh` for my `aarch64` Macbook.
During the bootstrap, `nix-darwin` installs the command `darwin-rebuild`, subsequent rebuilds should use `darwin-rebuild`.
Both `nix-darwin` and `darwin-rebuild` follows same semantics as `nixos-rebuild`, `test` for test activation, `build` for only building the configuration, `switch` for commit and activate etc.
## Install some packages
I have a set of packages that I like to have available system-wide (for all users). Add those to
`environment.systemPackages` in `configuration.nix`, which gives us:
```nix
{ config, lib, pkgs, ... }:
{
# List packages installed in system profile. To search by name, run:
# $ nix-env -qaP | grep wget
environment.systemPackages = with pkgs; [
vim
curl
gitAndTools.gitFull
mg
mosh
];
...
```
Activate with `darwin-rebuild switch --flake ~/path-to-config-directory`
## Home Manager
[home-manager](https://github.com/nix-community/home-manager) is a nix community project for managing user environments, it comes with a [tone of module for configuring more day-to-day user facing programs](https://home-manager-options.extranix.com/), for e.g the git module for configuring, well git.
# Expose the package set, including overlays, for convenience.
darwinPackages =
self.darwinConfigurations."MacBook-Pro".pkgs;
};
}
```
If you get an error `Error: HOME is set to "/Users/<username>" but we expect "/var/empty"`, make sure you have set `users.users.<username>.home` in configuration.nix.
## Manage homebrew applications
Sadly, nix still has some catching up to do with mac compatibility, biggest gripe for me was [accessing GUI apps with spotlight seems to need some workarounds](https://github.com/LnL7/nix-darwin/issues/214). Luckily brew solves this and we can just install applications with brew, still managed by nix.
`nix-darwin` can declaratively manage brew packages, however we need [nix-homebrew](https://github.com/zhaofengli/nix-homebrew) to install brew itself and manage the taps declaratively.
Grab nix-homebrew using flakes, and also add the taps itself as inputs, this maybe the most underrated flakes feature. We can pin the taps to a specific version!
```nix
inputs = {
nix-homebrew = {
url = "github:zhaofengli-wip/nix-homebrew";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
homebrew-core = {
url = "github:homebrew/homebrew-core";
flake = false;
};
homebrew-cask = {
url = "github:homebrew/homebrew-cask";
flake = false;
};
homebrew-bundle = {
url = "github:homebrew/homebrew-bundle";
flake = false;
};
```
.. and import the module into the system configuration.
```nix
nix-homebrew.darwinModules.nix-homebrew
{
nix-homebrew = {
enable = true;
# Apple Silicon Only: Also install Homebrew under the default Intel prefix for Rosetta 2
enableRosetta = true;
user = "username";
taps = {
"homebrew/homebrew-core" = homebrew-core;
"homebrew/homebrew-cask" = homebrew-cask;
"homebrew/homebrew-bundle" = homebrew-bundle;
};
mutableTaps = false;
};
}
```
The package installations are itself managed by nix-darwin, using `homebrew.*` options.
```nix
homebrew = {
enable = true;
global.autoIpdate = false;
casks = [ "kitty" ];
};
```
## Fin!
If you have followed through all of the above, like me, you should have a mac with almost everything configured declaratively, using nix.
This setup helps me share configuration with my other machines; they are just an `import` away! However this bootstrapping is neither simple nor short, that's definitly something to improve.
[^1]: Explaining flakes or nix nuanceses are out of scope and probably out of my reach, <https://nixos-and-flakes.thiscute.world/> is a better resource.
[^2]: For flake build system to find them, git should be aware of the files.