Skip to content

Instantly share code, notes, and snippets.

@astutecat
Last active September 16, 2024 12:44
Show Gist options
  • Save astutecat/30aa5eebb0765faa833e2cf019f559ee to your computer and use it in GitHub Desktop.
Save astutecat/30aa5eebb0765faa833e2cf019f559ee to your computer and use it in GitHub Desktop.
Notes on setting up Nix on MacOS.
#!/bin/sh
set -e
root_disk() {
diskutil info -plist /
}
apfs_volumes_for() {
disk=$1
diskutil apfs list -plist "$disk"
}
disk_identifier() {
xpath "/plist/dict/key[text()='ParentWholeDisk']/following-sibling::string[1]/text()" 2>/dev/null
}
volume_list_true() {
key=$1 t=$2
xpath "/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict/key[text()='$key']/following-sibling::true[1]" 2> /dev/null
}
volume_get_string() {
key=$1 i=$2
xpath "/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict[$i]/key[text()='$key']/following-sibling::string[1]/text()" 2> /dev/null
}
find_nix_volume() {
disk=$1
i=1
volumes=$(apfs_volumes_for "$disk")
while true; do
name=$(echo "$volumes" | volume_get_string "Name" "$i")
if [ -z "$name" ]; then
break
fi
case "$name" in
[Nn]ix*)
echo "$name"
break
;;
esac
i=$((i+1))
done
}
test_fstab() {
grep -q "/nix apfs rw" /etc/fstab 2>/dev/null
}
test_nix_symlink() {
[ -L "/nix" ] || grep -q "^nix." /etc/synthetic.conf 2>/dev/null
}
test_synthetic_conf() {
grep -q "^nix$" /etc/synthetic.conf 2>/dev/null
}
test_nix() {
test -d "/nix"
}
test_filevault() {
disk=$1
apfs_volumes_for "$disk" | volume_list_true FileVault | grep -q true || return
! sudo xartutil --list >/dev/null 2>/dev/null
}
main() {
(
echo ""
echo " ------------------------------------------------------------------ "
echo " | This installer will create a volume for the nix store and |"
echo " | configure it to mount at /nix. Follow these steps to uninstall. |"
echo " ------------------------------------------------------------------ "
echo ""
echo " 1. Remove the entry from fstab using 'sudo vifs'"
echo " 2. Destroy the data volume using 'diskutil apfs deleteVolume'"
echo " 3. Remove the 'nix' line from /etc/synthetic.conf or the file"
echo ""
) >&2
if test_nix_symlink; then
echo "error: /nix is a symlink, please remove it and make sure it's not in synthetic.conf (in which case a reboot is required)" >&2
echo " /nix -> $(readlink "/nix")" >&2
exit 2
fi
if ! test_synthetic_conf; then
echo "Configuring /etc/synthetic.conf..." >&2
echo nix | sudo tee /etc/synthetic.conf
if ! test_synthetic_conf; then
echo "error: failed to configure synthetic.conf" >&2
exit 1
fi
fi
if ! test_nix; then
echo "Creating mountpoint for /nix..." >&2
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true
if ! test_nix; then
sudo mkdir -p /nix 2>/dev/null || true
fi
if ! test_nix; then
echo "error: failed to bootstrap /nix, a reboot might be required" >&2
exit 1
fi
fi
disk=$(root_disk | disk_identifier)
volume=$(find_nix_volume "$disk")
if [ -z "$volume" ]; then
echo "Creating a Nix Store volume..." >&2
if test_filevault "$disk"; then
echo "error: FileVault detected, refusing to create unencrypted volume" >&2
echo "See https://nixos.org/nix/manual/#sect-apfs-volume-installation" >&2
exit 1
fi
sudo diskutil apfs addVolume "$disk" APFS 'Nix Store' -mountpoint /nix
volume="Nix Store"
else
echo "Using existing '$volume' volume" >&2
fi
if ! test_fstab; then
echo "Configuring /etc/fstab..." >&2
label=$(echo "$volume" | sed 's/ /\\040/g')
printf "\$a\nLABEL=%s /nix apfs rw,nobrowse\n.\nwq\n" "$label" | EDITOR=ed sudo vifs
fi
echo "" >&2
echo "The following options can be enabled to disable spotlight indexing" >&2
echo "of the volume, which might be desirable." >&2
echo "" >&2
echo " $ sudo mdutil -i off /nix" >&2
echo "" >&2
}
main "$@"
{ config, pkgs, ... }:
with pkgs;
let
inherit (pkgs) lorri;
elixir = beam.packages.erlangR22.elixir_1_9;
in
{
# List packages installed in system profile. To search by name, run:
# $ nix-env -qaP | grep wget
environment.systemPackages =
[
lorri
direnv
];
# Use a custom configuration.nix location.
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
# environment.darwinConfig = "$HOME/.config/nixpkgs/darwin/configuration.nix";
# Create /etc/bashrc that loads the nix-darwin environment.
programs.bash.enable = true;
programs.zsh.enable = true;
programs.fish.enable = true;
programs.zsh.promptInit = "";
# Used for backwards compatibility, please read the changelog before changing.
# $ darwin-rebuild changelog
system.stateVersion = 4;
# You should generally set this to the total number of logical cores in your system.
# $ sysctl -n hw.ncpu
nix.maxJobs = 4;
nix.buildCores = 4;
launchd.user.agents = {
"lorri" = {
serviceConfig = {
WorkingDirectory = (builtins.getEnv "HOME");
EnvironmentVariables = { };
KeepAlive = true;
RunAtLoad = true;
StandardOutPath = "/var/tmp/lorri.log";
StandardErrorPath = "/var/tmp/lorri.log";
};
script = ''
source ${config.system.build.setEnvironment}
exec ${lorri}/bin/lorri daemon
'';
};
};
}

Setting up Nix on MacOS

These instructions assume you're on osx Catalina or later44, which has the most problematic time when setting up nix because of the restriction on the root filesystem.

Creating a volume for the Nix Store

In this gist is a script (create-darwin-volume.sh) which will do the following:

  1. Create an APFS volume for the nix store
  2. Update /etc/fstab to mount the volume
  3. Use synthentic.conf to create /nix and point it to that volume.

This allows the nix store to exist with SIP enabled, without using a symlink.

  • After the script has run, you will need to reboot to allow the synthentic.conf changes to persist.

Install nix-darwin

This step is needed if you want to use lorri (strongly recommended), or if you want to install 'system' nix packages or daemons.

  • Create a darwin-configuration.nix file in ~/.nixpkgs/darwin-configuration.nix
    • There is an example config file in this gist which will install lorri.
  • Install nix-darwin by following its README:
nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A installer

./result/bin/darwin-installer

Use Lorri

To use lorri in a given location, type lorri init. This will create a .envrc and shell.nix. If they already exist, they will not be overwritten.

Type direnv allow in the directory and lorri will automatically (re)build the shell.nix for you when it's updated.

When you leave a directory, it'll unload the environment for you.

You can also install the direnv extension in visual studio code and it will make use of the environment too :)

Updating your system

To update, run nix-channel --update, followed by darwin-rebuild switch.

Clean out garbage with nix-collect-garbage once in a while.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment