
macOS で Nix home-manager を試す:CLI を宣言的に管理する
どうも!オペ部の西村祐二です!
仕事用 Mac、個人用 Mac に加えて、最近は仕事で Windows を触る機会も増え、複数の環境で開発まわりのセットアップを揃えるのに苦労する場面が増えていました。Mac を新しくするたびに Homebrew で何を入れていたか・dotfiles がどうなっていたかを思い出すのにも時間がかかります。設定を宣言的にコードへ落とし、世代管理して、いつでも前の状態に戻せる仕組みがあれば楽そうだと思い、最近よく名前を見かける Nix と home-manager に入門してみます。今回は nix-darwin を使わない standalone 構成を、 Flakes 前提で組みました。
入門記事なので、インストール・最小構成・CLI ツールの追加までを実際に動かしながら確認します。
この記事で扱う範囲は次のとおりです。
- macOS に Nix を入れる
- home-manager standalone を Flakes 構成で初期化する
- CLI ツールと git / fzf / neovim の設定を宣言的に追加する
- 入門時に踏みやすいハマりどころを整理する
- 合わなかったときの撤退・アンインストール方法を確認する
Nix と home-manager とは
Nix は、パッケージとシステム設定を「宣言的・再現可能・ロールバック可能」に扱うためのパッケージマネージャです。/nix/store に各パッケージのビルド成果物をハッシュ付きで保存し、ユーザー環境はその store へのシンボリックリンクの集まりとして表現されます。 Nixpkgs は多数のパッケージを抱えるリポジトリで、macOS でもそのまま使えます。
home-manager は、その Nix を使ってユーザーのホームディレクトリ(CLI ツール、dotfiles、環境変数など)を宣言的に管理するための仕組みです。home.nix というファイルに「git を入れて、こういう設定にしてほしい」と書くと、ホームディレクトリ配下にシンボリックリンクで配置してくれます。
home-manager の利用形態は3つあります(公式 README より)。
- standalone:
home-managerコマンド単体で動かす - NixOS モジュール: NixOS のシステム設定の一部として動かす
- nix-darwin モジュール: macOS の nix-darwin と組み合わせ、システム設定もまとめて宣言する
macOS で「最小構成から始めたい」「Homebrew は残したい」という今回のケースでは、standalone が出発点としてシンプルです。nix-darwin はシステム設定(macOS のデフォルト挙動・LaunchDaemon・Homebrew Cask 統合など)も Nix で扱える反面、最初に組むには学ぶことが増えるため、まず home-manager 単独でユーザー領域だけを宣言化し、必要を感じたら nix-darwin に進むという段階の踏み方が、自分には合っていそうでした。
主要な Nix コマンドの使い分け
home-manager を使っているうちは home-manager switch だけで完結することが多いですが、本記事中には素の Nix のコマンドもいくつか登場します。先に役割を整理しておくと、後の手順で「これ何のコマンド?」と止まらずに済みます。
| コマンド | 役割 | 本記事での主な出番 |
|---|---|---|
nix shell <flake>#<pkg> |
一時的にそのコマンドだけ呼べる shell に入る。exit するとホームには何も残らない |
新ツールを軽く試したいとき |
nix run <flake>#<output> |
flake が公開している実行可能パッケージを「呼ぶ」 | home-manager 自体の init / build / switch 起動 |
nix build <flake>#<output> |
パッケージをビルドして result/ リンクを作る。実行はしない |
自前 derivation のテストなど(本記事では home-manager build を経由する形で実質的に使う) |
nix develop <flake>#<devShell> |
プロジェクト固有の開発環境(依存ツール・環境変数)に入る | 言語ツールチェーンを mise や devShell に分担するとき |
nix flake update |
flake.lock を更新する。nix flake update <input> で対象を絞れる |
定期アップデートサイクル |
nix flake check |
flake outputs の評価とビルド可能性をチェックする | CI での最低限のチェック |
nix shell と nix run は似ていますが、 nix shell は shell に入る(複数コマンドを叩ける)、 nix run は そのパッケージを1回呼ぶ のが違いです。記事中で nix run home-manager/release-25.11 -- init が出てくるのは、 home-manager というパッケージを「呼んで init というサブコマンドを渡している」という意味になります。
試してみる
環境
- macOS(Apple Silicon / arm64)
- Determinate Nix 3.20.0(内包する Nix 本体は 2.34.6)
- home-manager
release-25.11ブランチ - シェル: zsh
1. Nix 本体をインストールする
macOS で Nix を入れる方法は2つあり、 公式インストーラ と Determinate Systems の nix-installer です。今回は後者を使いました。Determinate のインストーラには次のような特徴があります。
- Flakes 関連の experimental features(
nix-command flakes)が最初から有効になっている - macOS のメジャーアップグレードでインストールが壊れにくい
/nix/receipt.jsonにインストール記録が残り、/nix/nix-installer uninstallで素直にアンインストールできる
curl -fsSL https://install.determinate.systems/nix | sh -s -- install --determinate
実行すると、APFS の暗号化ボリュームを /nix にマウントし、ビルドユーザー(UID 351-382)を作成し、/etc/nix/nix.conf を配置し、zsh/bash/sh のプロファイルを書き換えてくれます。途中 sudo パスワードが求められます。
完了すると次のメッセージが出ます。
Nix was installed successfully!
To get started using Nix, open a new shell or run `. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh`
ここで出てくる /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh は、Nix を shell から使えるようにする初期化スクリプトです。nix コマンドや Nix で入れたコマンドに PATH を通し、NIX_PROFILES や NIX_SSL_CERT_FILE などの環境変数を設定します。
普段は新しいターミナルを開けば自動で読み込まれるため、毎回手で実行するものではありません。インストール直後に今開いている shell でそのまま nix を使いたい場合だけ、表示されたコマンドで一時的に読み込みます。
新しいターミナルを開いて、nix --version で確認できれば成功です。
$ nix --version
nix (Determinate Nix 3.20.0) 2.34.6
/etc/nix/nix.conf を覗くと、Determinate がどんな設定を入れたかが分かります(一部抜粋)。
extra-experimental-features = nix-command flakes
extra-substituters = https://install.determinate.systems
extra-experimental-features に flakes が入っているため、flake.nix 系のコマンドが追加設定なしで使えます。公式インストーラで入れた場合は、~/.config/nix/nix.conf などに同じ行を自分で追記する必要があります。
2. home-manager を初期化する
home-manager のリポジトリは nix-community/home-manager です。安定版の release-25.11 ブランチを使い、Flakes 構成を生成します。
ホームディレクトリ配下に作業ディレクトリを作って初期化します(~/.config/home-manager がデフォルトの探索場所ですが、今回は分かりやすく /tmp/hm-trial で試します)。
mkdir -p /tmp/hm-trial && cd /tmp/hm-trial
nix run home-manager/release-25.11 -- init /tmp/hm-trial
初回は home-manager 関連の依存をビルドキャッシュから落としてくる時間がかかりますが、最終的に flake.nix と home.nix の2ファイルが生成されます。
生成された flake.nix の中身です。
{
description = "Home Manager configuration of <your_username>";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{ nixpkgs, home-manager, ... }:
let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
in
{
homeConfigurations."<your_username>" = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [ ./home.nix ];
};
};
}
init の良いところは、Apple Silicon の Mac で実行すると system = "aarch64-darwin"; が自動的に設定されることです。Intel Mac なら x86_64-darwin が入ります。 inputs.nixpkgs.follows = "nixpkgs" も最初から書かれており、home-manager が参照する nixpkgs と自分が指定する nixpkgs を一本化してくれます(手書きで忘れると nixpkgs が二重ロックされて困るポイントです)。
ただし生成直後の flake.nix は nixos-unstable と home-manager の master を参照する設定になっています。安定版を使いたい場合は次のように nixpkgs を nixos-25.11 に、home-manager を release-25.11 に固定したほうが、後述のリリースノートに沿って動きを把握しやすいです。
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
公式マニュアルの Upgrading to a new Home Manager release でも、「standalone と nix-darwin では home-manager のブランチを Nixpkgs のブランチに合わせる」ことが推奨されています。
生成された home.nix は次のようになっています(コメントは省略)。
{ config, pkgs, ... }:
{
home.username = "<your_username>";
home.homeDirectory = "/Users/<your_username>";
home.stateVersion = "25.11";
home.packages = [ ];
home.file = { };
home.sessionVariables = { };
programs.home-manager.enable = true;
}
home.username と home.homeDirectory も自動で埋まっています。home.homeDirectory は macOS では /Users/<user> であり、Linux 系の /home/<user> をコピペしたまま使ってしまうと build 時にエラーになります。init 経由で生成しておけば、ここを間違える心配が減ります。
home.stateVersion は「いつ始めたか」を示す互換性アンカーで、 公式マニュアル でも「アップグレード時にむやみに変更しないこと」が言及されています。home-manager のリリースノートを読んで、変更が安全だと判断したときだけ更新します。
3. CLI ツールを宣言的に追加する
home.nix を編集して、普段使いの CLI ツールを追加してみます。home.packages でパッケージをインストールするだけの記法と、programs.<name> で「インストール + 設定ファイル生成」までセットでやる記法があります。
{ config, pkgs, ... }:
{
home.username = "<your_username>";
home.homeDirectory = "/Users/<your_username>";
home.stateVersion = "25.11";
programs.home-manager.enable = true;
home.packages = with pkgs; [
ripgrep
fd
];
programs.git = {
enable = true;
settings = {
user.name = "<your_name>";
user.email = "<your_email>";
};
};
programs.fzf = {
enable = true;
enableZshIntegration = true;
};
programs.neovim = {
enable = true;
defaultEditor = true;
viAlias = true;
vimAlias = true;
};
}
ポイントは次のとおりです。
home.packagesは「とりあえず PATH に入れたい」 CLI 向け(ripgrepfdなど)programs.gitは「git のインストールに加えて~/.config/git/configも生成してくれる」モジュールprograms.fzf.enableZshIntegrationを有効にすると、fzf の zsh 統合(Ctrl+R の履歴検索など)が自動で組み込まれるprograms.neovim.defaultEditor = trueでEDITOR環境変数の設定までセットになる
ホームディレクトリに反映する前に評価とビルドだけ通したい場合は home-manager build を使います。nix flake check は homeConfigurations.* を評価対象にしないため、設定ミスを拾う用途には home-manager build のほうが確実です。
$ nix run home-manager/release-25.11 -- build --flake .#<your_username>
--flake の引数で出てくる .#<your_username> の # は、 flake のどの output を使うか を指定する区切り文字です。. がカレントディレクトリの flake、# の右側が flake outputs 上の attribute path で、ここでは homeConfigurations."<your_username>" に対応します。# 以降を省略した場合、home-manager は username@hostname、続けて username の順で homeConfigurations を探索する仕様になっているので、構成が1つしかないうちは --flake . だけでも動きます。マルチホスト・マルチユーザーに広げる予定があるなら、最初から # で明示しておくと将来の自分が迷いません。
成功すると、カレントディレクトリに result/ というシンボリックリンクが作られます(このコマンドはホームディレクトリを書き換えません)。中を覗くと、ホームディレクトリに配置される予定のファイル群(home-files/.config/git/config など)と、PATH に入る予定のシンボリック群(home-path/bin/git、home-path/bin/rg など)を実物として確認できます。
$ cat result/home-files/.config/git/config
[user]
email = <your_email>
name = <your_name>
ここまでで build が通ったら、ホームディレクトリに反映する switch を実行します(既存ファイルとの衝突に備えて -b backup を付けると、衝突した既存ファイルを <file>.backup に退避してくれます)。
$ nix run home-manager/release-25.11 -- switch --flake .#<your_username> -b backup
switch がホームディレクトリに何をするか
switch が触るのは home.nix で宣言した範囲だけで、それ以外のファイルはそのまま残ります。今回のサンプル home.nix の場合、書き込み先はおおよそ次のようになります。
| home.nix の宣言 | 配置されるファイル | 既存ファイルとの衝突 |
|---|---|---|
home.packages = [ ripgrep fd ] |
~/.nix-profile/bin/{rg,fd} のシンボリックリンクが張られる |
ホーム配下のファイルは触らないため衝突なし |
programs.git.enable = true |
~/.config/git/config を生成 |
既存があれば ~/.config/git/config.backup に退避 |
programs.fzf.enable = true |
~/.nix-profile/bin/fzf と zsh 統合用の補助ファイル |
programs.zsh.enable を有効にしていないため .zshrc への注入は行われず、衝突は起きない |
programs.neovim.enable = true |
~/.config/nvim/init.lua を生成 |
既存があれば ~/.config/nvim/init.lua.backup に退避 |
defaultEditor = true |
EDITOR=nvim を hm-session-vars.sh 経由で設定 |
シェルがこのファイルを source していなければ反映されない |
~/.zshrc、~/.config/zellij/、~/.tmux.conf のような今回宣言していない dotfiles はそのまま動き続けます。-b backup 付きで実行した場合、衝突したファイルは .backup 拡張子で残るので、戻したくなったら手動でリネームし直せば元に戻せます(-b を付けずに実行すると、衝突した時点で Existing file '~/.config/git/config' is in the way のエラーで switch が中断します)。
ひとつ気をつけたいのは git の設定で、git は ~/.gitconfig と ~/.config/git/config の両方を読み、 同じキーがあれば ~/.gitconfig 側が優先されます。home-manager は XDG 準拠の ~/.config/git/config に書き込むため、既存の ~/.gitconfig を消さずに残しておくと、programs.git で書いた設定が ~/.gitconfig の内容に上書きされて反映されないように見える、という挙動が起きます。 git config --list --show-origin でどのファイルから読まれているかを確認するか、不要になった ~/.gitconfig は退避するのが無難です。
home-manager 管理下にあることを確認する
switch がうまくいったかどうかは、いくつかのコマンドで確かめられます。
インストール済みパッケージの一覧: home-manager packages で home-manager 経由で入ったパッケージを一覧表示できます。
$ home-manager packages
fd-10.4.2
fzf-0.72.0
git-2.53.0
home-manager-0-unstable
neovim-unwrapped-0.11.4
ripgrep-14.1.1
バイナリが Nix store 経由になっているか: type -a または which で、PATH 上の実体が ~/.nix-profile/bin/... から /nix/store/... に向いていることを確認できます。Homebrew 側のものが優先されている場合は、ここで気付けます。
$ type -a rg
rg is /Users/<your_username>/.nix-profile/bin/rg
$ readlink -f $(which rg)
/nix/store/...-ripgrep-14.1.1/bin/rg
which でも同じ確認ができますが、単独の which は PATH 上の 最初のヒットしか表示しません。Homebrew 側にも同名のものが入っていて先に勝っていると気付きにくいため、複数候補を見たいときは which -a(zsh の builtin、macOS の /usr/bin/which どちらも -a 対応)か type -a を使うと安全です。エイリアスやシェル関数で隠されている可能性まで確認したいなら、エイリアス・関数・builtin もまとめて教えてくれる type -a が情報量で勝ります。
$ which -a rg
/Users/<your_username>/.nix-profile/bin/rg
/opt/homebrew/bin/rg # Homebrew にも入っていればこの行が出る
設定ファイルが home-manager の symlink になっているか: ls -l で ~/.config/git/config などのリンク先が /nix/store/... に向いているかを確認できます。/nix/store/ が見えていれば home-manager 管理、自分で書いた普通のファイルなら管理外です。
$ ls -l ~/.config/git/config
lrwxr-xr-x ... ~/.config/git/config -> /nix/store/...-home-manager-files/.config/git/config
現在 active な世代と中身: home-manager generations で世代の履歴、nix profile list --profile ~/.local/state/nix/profiles/home-manager で現在の profile のリビジョン情報まで確認できます。
$ home-manager generations
2026-05-10 15:42 : id 1 -> /nix/store/...-home-manager-generation (current)
これらが期待どおりになっていれば、宣言した内容が実際にホームディレクトリへ反映されている状態です。逆にどれかが想定と違う場合(type -a で Homebrew のパスが先に出る、設定ファイルが symlink でない、など)、PATH 順や -b backup 退避の見落としを疑うサインになります。
4. 世代を確認する
switch のたびに新しい世代が記録されます。
$ home-manager generations
2026-05-10 15:42 : id 1 -> /nix/store/...-home-manager-generation (current)
直前の世代に戻すなら --rollback を付けて switch します(公式マニュアルの Rollbacks より)。
$ home-manager switch --rollback
build はせず、generation を1つ前のものに切り替えてくれます。「アップデートしたら zsh が起動しなくなった」みたいな時の最初の一手として覚えておきたいコマンドです。
ちなみに home-manager は news という仕組みでリリースノート更新を通知してくれます。switch のたびに「未読のお知らせが N 件あります」と表示されるので、home-manager news でざっと眺めるのが習慣化できそうです。
5. 新しいツールを追加するときの流れ
入門が終わったあとは「気になる CLI を見つけたら home.nix に足す」の繰り返しになります。基本フローと、最近増えている AI 系 CLI のように nixpkgs にまだ入っていないものをどう扱うかを整理します。
パッケージ名を調べる
Nixpkgs はパッケージ数が多く、想定と違う名前で登録されていることもあるので、まずは検索します。
-
Web 検索(おすすめ): search.nixos.org/packages で nixpkgs を絞って検索
-
CLI 検索:
$ nix search nixpkgs ripgrep * legacyPackages.aarch64-darwin.ripgrep (14.1.1) Utility that combines the usability of The Silver Searcher with the raw speed of grep -
home-manager のモジュールがあるかは home-manager Options Search で
programs.<name>を検索すると、書ける attribute がリストアップされます。programs.zellij、programs.gh、programs.batなど、メジャーなツールには専用モジュールが用意されています。
nix shell で先に試す
「気になるけど常用するか分からない」段階のツールは、 nix shell で一時的に呼べます。exit するとホームには何も残らず、何度も試せます。
$ nix shell nixpkgs#zellij
$ zellij --version
$ exit
良さそうなら home.nix に書く、合わなければ何も残らない、という試行サイクルを回せるのが Nix の便利なところです。
home.nix に追加して switch する
入れると決めたら、home.nix(または modules/packages.nix など切り出している場所)に足して home-manager switch するだけです。
バイナリを PATH に乗せるだけでよいケース:
home.packages = with pkgs; [
ripgrep
fd
zellij # 追加
];
設定ファイルもセットで宣言したい(home-manager にモジュールがある)ケース:
programs.zellij = {
enable = true;
enableZshIntegration = true;
settings = {
theme = "tokyo-night";
default_shell = "zsh";
};
};
home-manager packages や type -a zellij で入っていることを確認できれば完了です。
nixpkgs にあるけどバージョンが古いとき
nixpkgs を nixos-25.11 のような安定版ブランチに固定していると、ツールによっては stable に取り込まれているバージョンが少し古いことがあります。nix search nixpkgs <name> で出てきたバージョンが、上流の最新リリースと数ヶ月単位でずれている場合などです。
そういうときは nixpkgs-unstable を 別 input として追加 して、対象のパッケージだけ unstable 側から取ってくる構成にできます。stable をベースに据えつつ、ピンポイントで新しめの版が必要なものだけ unstable に逃がすイメージです。
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{ nixpkgs, nixpkgs-unstable, home-manager, ... }:
let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
in
{
homeConfigurations."<your_username>" = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
extraSpecialArgs = { inherit pkgs-unstable; };
modules = [ ./home.nix ];
};
};
extraSpecialArgs に pkgs-unstable を渡しておくと、home.nix 側で引数として受け取れるようになります。
{ config, pkgs, pkgs-unstable, ... }:
{
home.packages = with pkgs; [
ripgrep
fd
] ++ [
# この1つだけ unstable から取ってくる
pkgs-unstable.zellij
];
}
この書き方なら、 home.packages の大半は stable のままで、 unstable 由来のものだけが /nix/store/... の別ハッシュで入ります。 home-manager packages の出力でバージョンを見比べると、どちらから来ているかが分かります。
unstable 側の更新は nix flake update nixpkgs-unstable で対象を絞って取り込めます。stable はリリースサイクル、unstable は週次〜随時、というように追従ペースを分けられるのも、別 input として持っておくメリットです。
nixpkgs にないとき
探しているツールが nixpkgs に入っていれば、基本は home.packages に追加するだけで管理下に置けます。特に ripgrep、fd、gh、bat、zellij のような定番 CLI はまず search.nixos.org で探すのが早いです。
home.packages = with pkgs; [
gh
bat
zellij
];
一方で、更新が速い CLI や配布形態が特殊なツールは nixpkgs にまだ入っていないこともあります。その場合の選択肢は大きく4つです。
1. 公式の flake を input として取り込む
最近の OSS ツールは自前で flake.nix を持っていることが多いので、それを flake input として追加して使うのが綺麗です。例として openai/codex のリポジトリ直下の flake を取り込んでみます(Codex 自体は nixpkgs にも codex パッケージとして入っていますが、ここでは「上流の main を追いかけたい」「リリース直後の最新版を試したい」用途の例として、リポジトリの flake を直接参照する方を取り上げます)。
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
# OpenAI Codex CLI を上流の flake から取り込む
codex = {
url = "github:openai/codex";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, home-manager, codex, ... }: {
homeConfigurations."<your_username>" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.aarch64-darwin;
modules = [
./home.nix
({ ... }: {
home.packages = [ codex.packages.aarch64-darwin.default ];
})
];
};
};
openai/codex の flake は packages.<system>.default という output 名でビルド済みパッケージを公開しており、 aarch64-darwin / x86_64-darwin 両方に対応しています。上のように input として宣言し、home.packages から codex.packages.aarch64-darwin.default を参照するだけで codex コマンドが PATH に乗る状態になります。
flake input として固定されるため、flake.lock に commit hash が記録され、再現性も保てます。nix flake update codex のように対象を絞って更新できるので、 Codex だけを最新に追従しつつ、他は安定版に固定する という運用も自然にできます。
2. npm / pip 版を nixpkgs 経由で入れる
JS/TS 製のツールは、 pkgs.nodePackages.<name> で npm レジストリ経由のものを Nix 化できる場合があります。pip も同様に pkgs.python3Packages.<name> で扱えるものがあります。nix search nixpkgs nodePackages.<name> で確認できます。
3. 自前で derivation を書く
flake もない、npm でもない、というケース(プリビルドバイナリのみ配布など)は、 modules/<tool>.nix に fetchurl + stdenv.mkDerivation を書いて自分で derivation を作るのが正攻法です。やや学習コストはあります。
4. Nix の世代モデルと相性が悪いものは割り切る
「nixpkgs にも入っていない、上流 flake もない」だけでなく、 そもそも Nix の世代モデルと根本的に相性が悪いツール もあります。代表例が Claude Code です。
claude-code は nixpkgs にも入っていて(執筆時点で 2.1.133)、シンプルに home.packages に書けば動きそうに見えます。しかし anthropics/claude-code Issue #20012 で議論されているように、ネイティブインストーラは FHS 前提のハードコードパス(/usr/bin/bash など)を持ち、NixOS では破綻するという報告があります。macOS では /usr/bin/bash 自体は存在するので即座には壊れにくいものの、 Claude Code 本体の 背景での auto-update がホームディレクトリやストア配下に書き戻しに来る ため、Nix の read-only ストアと噛み合いません。issue は OPEN のまま、 npm 経由のリリースですら NixOS で壊れた事例(v2.1.113)が報告されており、コミュニティラッパー(sadjow/claude-code-nix)に頼る形が事実上の唯一の経路になっているのが現状です。
こういう 「Nix と本質的にぶつかる」タイプのツール は、無理に Nix 化すると追従の手間と不具合の調査コストが嵩みます。Claude Code については公式インストーラ(curl -fsSL https://claude.ai/install.sh | bash)に任せ、Nix 管理対象から外すのが現実的な落とし所になりそうです。
「最新バージョン追従」と「Nix 管理」
ここまでの4つの選択肢を見渡すと、判断軸は「最新を追いたいかどうか」だけではなく、 「Nix 経由の取り込み経路があるか」 と 「ツールが Nix の世代モデルと相性が良いか」 の2軸を独立に見るのが現実的です。たとえば Codex は 更新は活発だが、上流の flake を持っていて Nix と相性も良い ため、home-manager 管理のまま nix flake update codex で日次追従できます。「最新を追いたい = Nix を捨てる」が一般則ではない、ということです。
これを踏まえた、自分の線引きは次のようになります。
- 上流 flake を持つもの(例: Codex): 上流 flake を input に取り込んで home-manager 管理。
nix flake update <名前>で個別追従できるので、最新を追いつつ宣言性も保てる - nixpkgs にあって相性も良いもの(例: Playwright): nixpkgs 経由で home-manager 管理。
nix flake update nixpkgsの頻度(週1〜月1)で追う - nixpkgs stable だとバージョンが古いもの(例: zellij など更新が早い CLI):
nixpkgs-unstableを別 input として持ち、その1つだけpkgs-unstable.<name>で参照する。初回 build は重くなるが、stable をベースに据えたまま新しい版に追いつける - Nix の世代モデルと相性が悪いもの(例: Claude Code): 公式インストーラに任せる。auto-update が前提のツールはストアと衝突するため割り切る
「クリーンインストールから30分で同じ環境に戻したい」要件を重視するほど Nix 寄り、「新機能の出た日に即試したい」要件が強いほど公式インストーラ寄り、と自分のポリシーで割り切るのが続きやすそうです。
6. 特定のツールだけ Nix 管理から外す
入れてみたツールが合わなかったり、別の管理方法に戻したくなったときは、home.nix から該当ブロックを消して home-manager switch するだけで外せます。home-manager は 以前の世代で管理していて、新しい世代で管理しなくなったファイル を symlink から外したり削除したりしてくれます。
# 以下のブロックをまるごと削除
# programs.git = {
# enable = true;
# settings = {
# user.name = "<your_name>";
# user.email = "<your_email>";
# };
# };
$ home-manager switch --flake .#<your_username>
これで ~/.config/git/config の symlink が外れます。最初に -b backup で退避していた既存ファイルがあれば、リネームすれば元に戻せます。
$ ls -la ~/.config/git/config*
~/.config/git/config.backup
$ mv ~/.config/git/config.backup ~/.config/git/config
退避ファイルがないケース(home-manager 化する前にそのファイルが存在しなかった場合)は、何も戻すものがなく、シンプルに「Nix が触っていなかった状態」に戻ります。
撤退・アンインストール方法
home-manager だけやめたい場合と、Nix ごと消したい場合で手順が分かれます。
home-manager をアンインストールする(Nix は残す)
home-manager uninstall で、管理下のファイル削除・user profile からのパッケージ除去・全世代の削除(GC 対象化) まで一括で実行されます(公式 man page に標準サブコマンドとして記載)。
$ home-manager uninstall
$ nix-collect-garbage -d # /nix/store の掃除
実行後、-b backup で退避していたファイルがあれば元の名前に戻します。
$ for f in ~/.zshrc.backup ~/.config/git/config.backup; do
[ -e "$f" ] && mv "$f" "${f%.backup}"
done
~/.config/home-manager/ のリポジトリは残しても消してもOKです。nix コマンドそのものは残るので、 「home-manager は卒業するけど、たまに nix shell で一時的にツールを試したい」 という運用に切り替えられます。
Nix ごとアンインストールする
Determinate Systems のインストーラで入れた場合、 アンインストールも1コマンドで提供 されています。/nix/receipt.json をもとに、APFS ボリュームのアンマウント・/etc/synthetic.conf の編集・ビルドユーザーの削除・プロファイル行の除去まで自動でやってくれます。
$ /nix/nix-installer uninstall
公式インストーラ(sh <(curl ...))で入れた場合は手作業が増えますが、Determinate なら1コマンドで撤退できる点は、採用時の安心材料の1つになりそうです。事前に home-manager uninstall を済ませておくと、ホームディレクトリの symlink も綺麗になります。
撤退をスムーズにする普段の運用 Tips
- 初回 switch では
-b backupを付ける:退避ファイルが撤退時の主な戻し先になる ~/.gitconfigのような既存ファイルは消さずに残す:home-manager が書く側は~/.config/git/configなので、両立できる- Nix 化する前に dotfiles を git でコミットしておく:失敗時のロールバック先になる
「逃げ道がある」と分かっていると、新しいツールを programs.<name> で試す心理的ハードルもだいぶ下がります。
試してみた感想
programs.git・programs.fzf・programs.neovim などの「インストール + 設定ファイル」セットは思っていたより手軽で、これだけでも dotfiles の見通しがよくなりそうです。home-manager build で反映前の内容を確認でき、switch 後も世代として戻せるので、既存 dotfiles を少しずつ移す用途と相性が良いと感じました。
一方で、入門段階でも Homebrew と Nix を両方にインストールしていると PATH 衝突が起きやすく、type -a で勝ち負けを確認する癖をつけたほうが安全そうでした。secret や既存 dotfiles の移行は入門の延長で全部やろうとすると混乱しそうなので、home-manager に少し慣れてから線引きを決めるほうが良さそうな気配です。
まとめ
macOS で nix-darwin を使わない standalone home-manager + Flakes 構成を入門目線でひととおり試してみました。nix run home-manager/release-25.11 -- init で初期化すれば、aarch64-darwin や /Users/<user> といった Mac 固有のパス設定は自動で埋まるので、最初の取っ掛かりはある程度緩和されている印象でした。
次に試したいのは次のあたりです。
- 普段使いの zsh 設定(aliases、starship、direnv)を段階的に Nix 化する
- 自宅 Mac と仕事 Mac で同じ flake を共有する
homeConfigurations."<user>@<hostname>"構成を試す - agenix や 1Password CLI と組み合わせた secret 管理の運用に踏み込む
- 落ち着いたら nix-darwin に進んで、Homebrew や macOS のシステム設定までまとめて宣言化する
誰かの参考になれば幸いです。
参考リンク:






