nix-darwin一括installerの構築
概要
nix-darwinを導入する場合、通常はまずNix自体をインストールする必要があり、手順が煩雑になる。また、マシンの新設や初期化の際、既存と同じ環境を再現するために新しい環境で再ビルドを行うのは非効率である。
本記事では、Nixとnix-darwinのインストールを統合し、再ビルドを挟まずに既存環境を即座に再現可能なインストーラーの作成手法を解説する。
対象バージョン: Nix 2.29.2、nix-darwin 25.05
実装方針の確認
まず、既存のNixインストーラーの挙動を分析し、統合方針を整理する。
Nix公式インストーラーの仕組み
installerの実装はほとんどが以下に書かれている
install script→https://github.com/NixOS/nix/tree/2.29.2/scripts
binary-tarball→https://github.com/NixOS/nix/blob/2.29.2/packaging/binary-tarball.nix
上記の実装をしっかり睨むと大体以下のことがわかる
インストール対象となるderivationのクロージャ(closure)をすべてtarボールに固めている。
インストールスクリプト(install-multi-user.sh)は、ビルド用ユーザー/グループの作成、/nix/store へのデリベーションのコピー、nix-store --load-db によるDB復元、およびnix-daemonの登録等を行う。
https://github.com/NixOS/nix/blob/2.29.2/scripts/install-multi-user.sh#L976
以下は今回の統合に不要なので削除する
nix-channel を登録している
nix-env -i でroot userのdefault profileに nixをインストールしている
shellのprofileを変更
nix-daemonをLaunchDaemonsに登録している
nix-darwinのインストール手順
一方nix-darwinのinstall方式を見ていくと以下のことがわかる
/etc/nix-darwinに設定をcopy
darwin-rebuild switch
build
profile に追加
activate を実行
上記の二つをまとめると、
現在の設定(=darwinSystem)を全てを含んだtarを作成
install script の中で諸々の処理の代わりにactivateを実行
darwin-rebuild switchによりprofileを作成
すればいいということがわかる
darwinSystemの構成と整合性
インストーラーに含める darwinSystem のderivationは、インストール後に /etc/nix-darwin/flake.nix から再現されるものと完全に一致させる必要がある。これらが異なると、インストール直後に再ビルドが走ってしまうためだ。
これは、「現在のnix-darwinのprofileに入っているもの=darwinConfiguration」をtarの中に含めてもそれが必ずしも再現可能な構成を取れるかというと別ということである
この整合性を保つため、/etc/nix-darwin/flake.nix からdotfiles管理用のGitリポジトリを inputs として参照する構成をとる。
code:/etc/nix-darwin/flake.nix
{
inputs = {
dotfile.url = "github:yourname/dotfiles";
};
outputs = { self, nix-darwin, dotfile, ... }: {
darwinConfigurations."default" = nix-darwin.lib.darwinSystem {
modules = dotfile.darwinModules.default ;
};
};
}
以上のようにdarwinModuleをexportする形で構成しておくと非常に都合がいい
余談だが、nixにflakeが導入されて移行 /etc/nixos/configuration.nix の代わりにdotfile repositoryの中で nixosConfigurationを直接管理することがしやすくなったが、新しいマシンをセットアップしてみるとこれが実際に嬉しいかというとそうでもなかったりする。
なので、/etc/nixos/ 以下や /etc/nix-darwinはgitで管理せずにdotfile側でmoduleをexportする形が嬉しいのではないかと思う
code:/etc/nix-dawin/flake.nix
darwinConfigurations."default" = dotfile.darwinConfigurations.default
以上のように、dotfile 管理用 git repositoryの中にdarwinConfigurationsを書いてしまうと少し問題が起きる可能性がある。
できなくはないが、少しでも依存を間違えるとderivationが異なってしまうだろう
インストーラーの作成とパッチ適用
Nixのインストール処理のうち、nix-darwinの activate で代替可能な箇所(プロファイル作成やnix-daemon登録など)を削除・変更する。
activate: 同梱した darwinSystem の実行。
設定コピー: /etc/nix-darwin への配置。
darwin-rebuild: $SELF(インストーラーのソース)を --override-input で指定して実行し、derivationの整合性を確保する。
patchファイルは以下のようになる
code:installer.patch
diff --git i/packaging/binary-tarball.nix w/packaging/binary-tarball.nix
index 2050384b0..d91b6bb8e 100644
--- i/packaging/binary-tarball.nix
+++ w/packaging/binary-tarball.nix
@@ -4,6 +4,8 @@
buildPackages,
cacert,
nix,
+ darwinSystem,
+ self,
}:
let
@@ -12,6 +14,8 @@ let
rootPaths = [
nix
cacert
+ darwinSystem.system
+ self
];
};
@@ -32,6 +36,8 @@ runCommand "nix-binary-tarball-${version}" env ''
--subst-var-by cacert ${cacert}
substitute ${../scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
+ --subst-var-by darwin_system ${darwinSystem.system} \
+ --subst-var-by self ${self} \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${../scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
diff --git i/scripts/install-darwin-multi-user.sh w/scripts/install-darwin-multi-user.sh
index c74999512..5e2c5fc42 100644
--- i/scripts/install-darwin-multi-user.sh
+++ w/scripts/install-darwin-multi-user.sh
@@ -21,6 +21,9 @@ readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
# create by default; set 0 to DIY, use a symlink, etc.
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
+readonly DARWIN_SYSTEM="@darwin_system@"
+readonly SELF="@self@"
+
# caution: may update times on / if not run as normal non-root user
read_only_root() {
# this touch command ~should~ always produce an error
@@ -121,6 +124,17 @@ poly_configure_nix_daemon_service() {
launchctl kickstart -k system/org.nixos.nix-daemon
}
+poly_configure_nix_daemon_service() {
+ _sudo "activate" \
+ "$DARWIN_SYSTEM"/activate
+
+ _sudo "copy directory" \
+ /bin/cp -R "$SELF"/nix-darwin/ /etc/nix-darwin
+
+ _sudo "darwin-rebuild switch" \
+ env "PATH=$DARWIN_SYSTEM/sw/bin:$PATH" "$DARWIN_SYSTEM"/sw/bin/darwin-rebuild switch --flake /etc/nix-darwin#default --override-input nix-darwin-installer "$SELF"
+}
+
poly_group_exists() {
/usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
}
diff --git i/scripts/install-multi-user.sh w/scripts/install-multi-user.sh
index f051ccc46..a55d2fc43 100644
--- i/scripts/install-multi-user.sh
+++ w/scripts/install-multi-user.sh
@@ -1016,19 +1016,8 @@ main() {
create_build_group
create_build_users
create_directories
- place_channel_configuration
install_from_extracted_nix
- configure_shell_profile
-
- set +eu
- # shellcheck disable=SC1091
- . /etc/profile
- set -eu
-
- setup_default_profile
- place_nix_configuration
-
poly_configure_nix_daemon_service
trap finish_success EXIT
この --override-input を活用する手法により、確実に同一のderivationを再現できる。
検証と注意点
今回作成したソースコードは https://github.com/hiroqn/nix-darwin-installer にあるので、#installerをビルドすることでどのようなものがビルドされるか確認してもらうことができる
nixのinstallのテストは普段使っている環境を毎回吹っ飛ばすわけにはいかないので、UTM app 等の仮想環境を用いてmacOS上でのクリーンインストールテストを行うことを推奨する。
余談だが、最近macOSのセキュリティ機能のgatekeeperがかなり厳しく、ブラウザからダウンロードしたものは実行できないようになっている。インストール中に kill 9 で停止することがある。その場合は xattr -c コマンドでメタデータを削除する必要がある。
謝辞:今回の検証は https://github.com/ynishinaka 氏の社内での検証を元にしている。