開発環境の再現性100%へ!Nixで実現する失敗しないオンボーディング

はじめまして!プロダクト開発部の菊池です。普段はOptimal Remoteというプロダクトの開発をしています。

本記事はOPTiM TECH BLOG Advent Calendar 2025 Day 16 の記事です。

はじめに

皆さんは「開発環境の再現」で困った経験はございますか?新卒たる私は、今年度初めに多々経験しました。プロジェクトのみならず、PC環境自体の再現も妙な手間が発生しがちですよね。dotfilesを共通化したい、Homebrew等のインストールをそれぞれのPCでし直すのは大変...等々。

Nixはこの問題の根本解決を目指す純粋関数型パッケージマネージャーです。

本記事では私がUbuntuとmacOSでNix、およびその派生であるhome-manager、nix-darwinを使ってみた経験を元に、

  • Nix、home-manager、nix-darwinの概要
  • 使ってみた感触、注意点

について、当社の社内勉強会の内容をベースにご紹介します。

(NixOSは未導入・未経験のため記載しておりません)

TL;DR

  • Nixは宣言的・再現性・信頼性を担保するパッケージマネージャー。ビルド時の入力とツール、ビルド環境の固定を徹底する。
  • 公式のパッケージリポジトリで膨大な量のライブラリやツールが提供されている。
  • devShellという、必要なライブラリや実行バイナリがその中のみで有効化されるシェルを定義することができる。開発環境の均一化ができる。
  • home-managerを使用し、ホームディレクトリを丸ごと宣言的に管理することができる。macOSの場合、nix-darwinでOS設定も管理できる。PCの環境移行が容易。
  • 英語の情報は豊富で、ChatGPTにも十分手伝ってもらえる。日本での知名度の低さと、関数型言語に由来する書き方のクセが短所。

Nixの3本柱

Nixが「純粋関数型」と呼ばれる理由は、入力とビルド方法を徹底的に固定化する仕組みにあります。このために次の3点が整備されています。

宣言的(Declarative)

ビルドの手順や依存関係をテキスト(Nix言語)で明示的に記述します。

毎回のビルドでコマンド実行順に依存せず、環境構築がコード化されます。

再現性(Reproducibility)

依存ライブラリ・設定・ビルドスクリプトまで、入力と呼べるものはすべてハッシュ化されます

同じ入力・アーキテクチャ・OSを用意しNixファイルを評価すれば、どのマシンでも完全に同じバイナリが生成されます。

信頼性(Reliability)

Nixのビルドは隔離されたサンドボックス内で行われ、暗黙の依存が混入しないように徹底されています。

/nix/store

Nixをインストールすると、システムに/nix/storeというディレクトリが生成されます。

ここには、ソースコード・ビルドツール・生成バイナリなど、すべての成果物が

/nix/store/<ハッシュ>-<名前>-<バージョン>

例えばlibcurlをNix上でビルドした場合、成果物それ自身、およびそれが動的リンクする依存ライブラリもすべて/nix/store内に固定されます。

$ readlink result
/nix/store/419w943samwgl9j4w0nfkhlaqyig72j1-curl
$ ldd result/bin/curl
libcurl.so.4 => /nix/store/...-curl/lib/libcurl.so.4
libpsl.so.5  => /nix/store/...-libpsl-0.21.5/lib/libpsl.so.5
libssl.so.3  => /nix/store/...-openssl-3.5.1/lib/libssl.so.3
...

どのビルド成果物からも依存されないストア要素はnix store gcというコマンドで安全に削除できます。不要なファイルがディスクを圧迫することを避けられます。

DerivationとRealization

Nixのビルドは大きく次の2段階で行われます。

Nix言語(.nix / flake.nix)
        ↓ Evaluation
Derivation(.drv)
        ↓ Realization(実際のビルド)
ビルド成果物(/nix/store/<hash>-<name>-<version>)

Derivation

パッケージをどうビルドするかの手順書です。これには

  • 依存ライブラリのハッシュ
  • ビルドツールのハッシュ
  • 環境変数

などが含まれます。nixファイルの役割はこのDerivationを定義することです。ビルトインやライブラリには様々なビルド方法をサポートする関数がありますが、最終的には最も原始的なDerivation定義関数にたどり着きます。実はこれ自身も/nix/storeに生成されます。

Realization

Derivationを元にサンドボックス環境でビルドが実行され、成果物が/nix/storeに生成されます。

依存ライブラリやビルドツールがまだ/nix/storeにない場合、それ自身のビルドを再帰的に行います。

lockファイル

Derivationの際ハッシュ値が固定されますが、このハッシュ値の計算が個々人によりブレることはないのでしょうか?nixファイルでハッシュ値をすべて指定するべきなのでしょうか?

Derivationの作成の際、lockファイルというファイルがなければ新規作成されます。この際外部ソースの取得を行い、ハッシュ値や取得URLを解決し、lockファイルに記述します。

Derivationの作成の際lockファイルがあれば、そこに記載されたハッシュ値や取得URLを元にDerivationが行われます。nixファイルと一緒にlockファイルを渡せば、そこから下流のステップはすべて環境によらず共通化されます。

libcurlをNixでビルドする

シンプルな例として、curlをNixでビルドしてみます。

{
  inputs = {
    # 1. nixpkgs(公式リポジトリ)を取得
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
  };

  outputs = {
    self,
    nixpkgs,
  }:
  let
    system = "x86_64-linux";
    # 2. nixpkgsのうち、使用するOS・アーキテクチャをx86_64・linuxに限定
    pkgs = nixpkgs.legacyPackages.${system};

    # 3. ソースコードのダウンロードURLとダウンロード物のハッシュ値を固定
    srcTarball = builtins.fetchTarball {
      url = "https://curl.se/download/curl-8.16.0.tar.xz";
      sha256 = "sha256:12nrn2iv9qya5a53ihhalvasfs5inxz2klw1y9kfhyhmwzgar9hk";
    };
  in {
    # 4. 「nix build」コマンドでRealizationするDerivationを定義する
    packages.${system}.default = pkgs.stdenv.mkDerivation {
      name = "curl";
      # 5. 入力にダウンロードしたtar.xzを指定
      src = srcTarball;
      enableParallelBuilding = true;
      doCheck = false;

      # 6. nixpkgsからビルドの際の依存を指定
      nativeBuildInputs = [
        pkgs.openssl
        pkgs.libpsl
      ];
      configureFlags = [
        "--with-openssl=${pkgs.openssl.dev}"
        "--with-libpsl"
      ];
    };
  };
}

mkDerivationでは、Derivationを8つのステップに分けて定義します。それぞれのステップには標準動作が備わっているため、設定のみ変更し手順は標準に則っています。

Nixにおけるパッケージリポジトリ

先の実例に含まれていたgithub:nixos/nixpkgs/nixpkgs-unstableですが、これはよく使われるライブラリやアプリケーションの各OS・アーキテクチャごとのビルド成果物を頒布しているNix公式のパッケージリポジトリです。約9万個のパッケージを提供しています。パッと思いつく大体のものはここに含まれているのではないでしょうか。私はそうでした。

nixpkgs-unstableはテスト済みの中で最も新しいブランチで、日単位で更新されます。LTSにあたるnixos-25.11もあります。

集団開発でありがたいことに、このようなパッケージリポジトリを自作する方法もあります。変更頻度は低いのにビルドの時間が長かったり手間がかかったりするものは、ここに登録しておけば会社のみんなで使いまわせます。

devShell

ここまではビルドに関する設定でしたが、devShellという開発環境を均一化する方法も整備されています。

devShellには

  • シェル起動時パスに追加するパッケージ
  • シェル起動時のフック

などを指定できます。前述の通りNixには豊富なパッケージリポジトリがあるので、パッケージを指定する際もnixpkgs-unstable等から必要なものを引っ張ってくるだけで完了します。

例えば下記は私のWeb開発用のリポジトリから引っ張ってきたものですが、node系統の指定とE2E用のplaywrightの指定が、実質的には20行未満で済んでしまい驚きました。

    devShells = gen systems (system:
      let
        pkgs = import nixpkgs { inherit system; };
      in {
        default = pkgs.mkShell {
          packages = with pkgs; [
            nodejs_24
            nodePackages.pnpm
            playwright-driver.browsers
          ];
          shellHook = ''
            echo "entered devShell"

            export PATH="node_modules/.bin:$PATH"

            # playwrightにNixのパスを渡す
            export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers}
            export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true
            export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true
          '';
        };
      }
    );

nix developでdevShellを起動すると、Node.jsやpnpmをグローバルインストールしていなくても、シェル内ではそれらを使えます。

ホームディレクトリとOSの設定の管理

ここまではビルド・開発環境の管理についてでしたが、nixの派生であるhome-managerを使うとホームディレクトリそのものを宣言的に管理することができます。dotfiles、エディタ設定、GUIアプリ、PATH・環境変数などです。

またmacOSの場合、nix-darwinを使用するとOSの設定も宣言的に管理することができます。画面の設定、Dockの表示内容、Homebrewでのインストールするパッケージなどです。

ここまでのセットアップを行うと、新しいパソコンの設定再現が

  • Nix等のインストール
  • 自分のGitHubからリポジトリをダウンロード
  • nix run .#updateを実行

で済むようになります。3行で済んでしまいますがこれで本当に元のPCと同じ環境が再現されます。実際に私も個人用のUbuntu/macOSを元に作成した設定を、フォークで一部改変しつつ仕事用のPCに被せることに成功しました。

入門してみての感想

情報は割とある

日本語の詳細な情報はやや少ないですが、公式が出している英語ドキュメントがちゃんとしており、

  • ビルトイン関数の使い方
  • nixpkgsにどのようなパッケージが含まれるか
  • 各パッケージにどのような設定項目があるか

などの点は網羅されていたと思います。Redditでも数万人単位のフォロワーがついており、十分コミュニティから注目されていると言えると考えています。

nixpkgsのパッケージが豊富すぎる

約9万個の謳い文句は伊達じゃないです。home-managerとnix-darwinで色々GUIアプリやzshプラグイン、neovimプラグインを入れてみましたが、初心者の私が思いつくようなものはすべてnixpkgsに完備されていました。自分でビルドしたものは1個もありませんでした。それらがちゃんと動いているのも当然ですがすごいものです。

ChatGPTがちゃんとNix言語を理解している

初心者の私としてはありがたかった点です。Nix言語自体は理解しているようなので、エラーを投げると大体解決してもらえました。ただし文法以外の点の認識が古いこともあるので、インターネット検索をONにするのがおすすめです。

ユーザー名やホームディレクトリの取り回しに難あり

ここまでお話しした内容はOSのセットアップが済んだ上で実行することなので、ユーザー名やホームディレクトリの設定などがOS設定とnixファイルで違うとエラーになります。この点を認識していなかったため、作成したnixを他PCに適用しようとした時やや手間取りました。

知名度や書き方のクセ

私はNixは好きなのですが、日本での知名度の低さは否めません。またNix言語も関数型言語にルーツを持つため、書き方にクセが大いにあります。

その点を踏まえても魅力的なコンセプトだと思いますので、是非とも広がってほしいと思っています。

あとがき

Nix自体について、その活用法についてなど、まだまだ発掘の余地は多くあると思いますが、「すごい!」という熱量だけでも伝わればと思い今回アドベントカレンダーに投稿いたしました。まずは一つ、日本語の解説だけでも読んでいただければ幸いです。

そして何より、このNixについてはOJTの方に教えていただきました。日頃からチームで幅広い技術レンジに携わることができ、技術者冥利に尽きるというものです。Webだけじゃない、ネイティブもバックエンドもインフラも興味がある方、一度下記をチェックしてみませんか?

www.optim.co.jp

参考資料

概念と実践のベースとして、一番参考にさせていただいたサイトです。

既存のmacOSにNixを被せる点で参考にさせていただきました。

nixpkgsにどのようなリポジトリが登録されているかを検索できます。

home-manager、nix-darwinで設定可能な項目の一覧です。