Nixを理解する

「Nix」という名前を聞いたことがあるだろうか。dotfiles管理のHome-Manager、開発環境構築のdevenv、あるいはNixOSというLinuxディストリビューション。これらはすべて「Nixエコシステム」の一部だ。

dotfiles管理のモダンプラクティス2026でHome-Managerを紹介したが、その背後にある思想を理解しないと、なぜわざわざ学習コストの高いNixを使うのかがわからない。この記事では、Nixの核心にある「純粋関数型パッケージ管理」という思想と、それが解決する問題を掘り下げる。

なぜNixが存在するのか

従来のパッケージ管理には、長年解決されていない問題がある。

依存関係地獄(Dependency Hell) - パッケージAはライブラリXのv1.0を要求し、パッケージBは同じライブラリXのv2.0を要求する。両方を同時にインストールできない。

環境差異 - 「私のマシンでは動く」問題。開発環境、CI環境、本番環境で微妙にバージョンが異なり、再現不能なバグが発生する。

壊れるアップグレード - パッケージを更新したら、依存していた別のパッケージが動かなくなった。しかも元に戻す方法がない。

暗黙の依存関係 - ビルドが成功したが、実は環境にたまたま入っていたツールに依存していた。別の環境では動かない。

これらの問題の根本原因は、従来のパッケージ管理がミュータブル(可変) であることだ。/usr/bin/pythonというパスは一つしかなく、アップグレードすると上書きされる。

Nixは、この問題を**イミュータブル(不変)** なアプローチで解決しようとしている。

Nixとは何か:3つの顔

「Nix」という言葉は、文脈によって3つの異なるものを指す。

generated_imgs/nix-ecosystem.png

Nix言語

Nixの基盤にあるのは、純粋関数型の設定記述言語(DSL)だ。JSONに似た見た目だが、関数、変数、条件分岐、遅延評価を持つ。

{
  # 基本的な属性セット(JSONのオブジェクトに相当)
  name = "my-project";
  version = "1.0.0";

  # リスト
  dependencies = [ "nodejs" "git" ];

  # 条件分岐
  shell = if pkgs.stdenv.isDarwin then "/bin/zsh" else "/bin/bash";

  # 関数
  greet = name: "Hello, ${name}!";
}

この言語の特徴は純粋性だ。同じ入力には必ず同じ出力が返る。副作用がない。これがNixの再現性を支える根幹になっている。

Nixパッケージマネージャ

Nix言語で書かれたパッケージ定義を解釈し、実際にビルド・インストールを行うツール。2003年にEelco Dolstraによって開発された。

従来のパッケージマネージャ(apt、Homebrew等)とは根本的に異なるアプローチを取る。詳細は後述する。

NixOS / Home-Manager / Flakes

Nixエコシステムの上に構築されたツール群:

重要なのは、NixOSを使わなくてもNixは使えるということだ。macOSやUbuntuにNixパッケージマネージャだけをインストールし、Home-Managerでdotfilesを管理する、という使い方も可能。

純粋関数型アプローチ:なぜ「関数」なのか

Nixの核心を一言で表すと「パッケージを関数の戻り値として扱う」ということだ。

従来のパッケージ管理では、apt install firefoxを実行すると、システムのどこかにFirefoxがインストールされる。そのパスは固定で、バージョンを上げると上書きされる。

Nixでは、パッケージは「入力(ソースコード、依存関係、ビルドスクリプト等)を受け取り、出力(ビルド成果物)を返す関数」として定義される。

パッケージ = f(ソースコード, 依存関係, ビルドスクリプト, ...)

関数型プログラミングの「純粋関数」と同様に、同じ入力からは必ず同じ出力が得られる。これがNixの再現性の源泉だ。

Nix Storeの仕組み:ハッシュが保証する再現性

Nixのパッケージは、/nix/store/という特殊なディレクトリに格納される。

/nix/store/5rnfzla9kcx4mj5zdc7nlnv8na1najvg-firefox-120.0/

この5rnfzla9...という文字列は、パッケージのすべての入力から計算されたハッシュだ。これはContent-Addressable Storageと呼ばれる方式で、データの内容からアドレスを決定する。

generated_imgs/nix-store-hash.png

ハッシュの計算には以下が含まれる:

入力が1ビットでも変われば、ハッシュも変わり、出力パスも変わる

この仕組みがもたらす帰結:

複数バージョンの共存

/nix/store/abc123...-python-3.9.0/
/nix/store/def456...-python-3.10.0/
/nix/store/ghi789...-python-3.11.0/

パスが異なるため、異なるバージョンが干渉せず共存できる。あるプロジェクトはPython 3.9を、別のプロジェクトはPython 3.11を使う、ということが自然にできる。

アトミックなアップグレードとロールバック

パッケージを更新しても、古いバージョンは/nix/store/に残っている。問題があれば、参照先を戻すだけで以前の状態に復帰できる。アップグレードの途中で電源が落ちても、システムが中途半端な状態になることはない。

再現性の保証

同じflake.lock(依存関係のロックファイル)からは、どのマシンでも同じパッケージがビルドされる。「私のマシンでは動く」問題が原理的に発生しない。

generated_imgs/nix-mutable-vs-immutable.png

Nixが有効なユースケース

Nixの学習コストは高い。どんな場面でその投資が報われるのか。

開発環境の統一

チームメンバー全員が同じ開発環境を使うことを保証できる。

# flake.nix
{
  outputs = { self, nixpkgs }: {
    devShells.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
      buildInputs = [
        nixpkgs.legacyPackages.x86_64-linux.nodejs_20
        nixpkgs.legacyPackages.x86_64-linux.yarn
        nixpkgs.legacyPackages.x86_64-linux.postgresql_15
      ];
    };
  };
}

新しいメンバーがジョインしたら、nix developを実行するだけで環境が整う。READMEに「Node.js 20をインストールしてください」と書く必要がない。

CI/CDでの再現性

ローカルで通ったテストがCIで落ちる、という問題を減らせる。CIもローカルも同じNix定義から環境を構築するため、環境差異が生じにくい。

複数言語・複数ツールの統合管理

Node.js、Python、Rust、Go...プロジェクトごとに異なる言語スタックを使う場合でも、Nixで統一的に管理できる。asdfやmiseのようなバージョンマネージャを言語ごとに使い分ける必要がない。

セキュリティ・コンプライアンス

「このシステムには何がインストールされているか」を正確に追跡できる。ハッシュベースの管理により、意図しない変更が混入していないことを検証できる。金融機関などセキュリティ要件が厳しい環境で採用されている理由の一つ。

過去の環境の再現

「3年前のプロジェクトを、当時と同じ環境で動かしたい」という要求に応えられる。flake.lockがあれば、当時のNixpkgsのリビジョンから当時のパッケージバージョンを再現できる。

Nix vs 他のアプローチ

vs Homebrew / apt

generated_imgs/nix-mutable-vs-immutable.png

観点 Homebrew / apt Nix
ストレージモデル ミュータブル(上書き) イミュータブル(追加のみ)
複数バージョン 困難 自然にサポート
ロールバック 困難 容易
再現性 保証なし 保証あり
学習コスト

Homebrewは「手軽にパッケージをインストールしたい」という用途には最適だ。しかし「チーム全員で同じ環境を使いたい」「1年後に同じ環境を再現したい」という要求には応えにくい。

vs Docker

generated_imgs/nix-vs-docker.png

NixとDockerは競合ではなく、補完関係にある。

観点 Docker Nix
再現性の対象 ランタイム環境 ビルドプロセス
決定論性 ビルド時のネットワーク等で変動 同じ入力 = 同じ出力
主な用途 デプロイ、実行環境の隔離 開発環境、ビルド環境
組み合わせ NixでDockerイメージをビルド可能 -

DockerfileのRUN apt-get update && apt-get install -y python3は、実行するタイミングによって異なるバージョンのPythonがインストールされうる。Nixでビルドしたイメージなら、常に同じバージョンが入る。

実際、「NixでDockerイメージをビルドする」という併用パターンは広く使われている。

vs asdf / mise

asdfやmiseは「言語ごとのバージョン管理」を行うツールだ。.tool-versionsファイルでNode.js 20、Python 3.11といったバージョンを指定する。

Nixはより広いスコープを持つ。言語ランタイムだけでなく、データベース、CLIツール、システムライブラリまで同じ仕組みで管理できる。ただし、その分学習コストも高い。

「Node.jsのバージョンだけ揃えたい」ならasdf/miseで十分かもしれない。「開発に必要なすべてのツールを宣言的に管理したい」ならNixが向いている。

Nixの代償:学習コストの実態

Nixを使うかどうかの判断で最も重要なのは、学習コストとの見合いだ。

急峻な学習曲線

Nixの学習曲線は「山」と表現されることが多い。

日常の摩擦

Nixを使いこなせるようになっても、日常的な摩擦は残る。

「すべてをNixで」vs「部分的に導入」

Nixを全面採用するか、部分的に使うかは重要な選択だ。

全面採用(NixOS + Home-Manager)

部分的導入(既存OS + Nix開発環境)

多くの場合、部分的導入から始めて、価値を感じたら範囲を広げていくのが現実的だ。

Nixを学ぶべき人、学ばなくてよい人

学ぶ価値が高い人

無理に学ぶ必要がない人

まとめ

Nixは「パッケージをイミュータブルに、純粋関数的に管理する」という思想に基づいたエコシステムだ。

従来のパッケージ管理の問題(依存関係地獄、環境差異、壊れるアップグレード)を、ハッシュベースのストレージと宣言的プログラミングで解決しようとしている。

学習コストは高いが、一度習得すれば「環境は解決済みの問題」になる。Shopify、Replit、Andurilといった企業が本番環境で採用していることからも、その価値は実証されている。

すべての人にNixが必要なわけではない。しかし「再現性」と「宣言的管理」に価値を感じるなら、投資する価値のあるエコシステムだと思う。

次のステップ

Nixに興味を持ったら、以下のリソースから始めるのがおすすめだ。

参考リンク

抽出された概念