メインコンテンツへスキップ
中級11分で読める

Nixで開発環境を構築する(nix-shell / flakes 実践)

Nixで再現性の高い開発環境を構築する方法を、nix-shellやflakesの使い方、Node.js環境の例まで実例を交えてわかりやすく解説します。

🎯 この記事で学べること

  • 1
    なぜNixで開発環境を作ると「壊れない」のかを理解できます
  • 2
    nix-shellで一時的な開発環境をサッと立ち上げる方法がわかります
  • 3
    shell.nixでチーム全員が同じ環境を再現する方法を学べます
  • 4
    flakesによるモダンで完全に固定された環境管理を理解できます
  • 5
    Node.js環境の具体例とDockerとの使い分けを把握できます

読了時間: 約11

生徒

新しいPCに開発環境を入れ直したら、またバージョンがズレて動かない... Node.jsもPythonも、READMEの手順どおりやったのに。

ユニコーン先生

あるあるだね。それ、Nixで開発環境を作ると かなり解決できるよ。プロジェクトに設定ファイルを1個置くだけで、 誰のPCでも同じ環境が立ち上がるんだ。

生徒

え、1個のファイルで?でもNixって難しそうな イメージあるんだけど...

ユニコーン先生

OS全部をNixで管理しようとすると大変だけど、 「開発環境だけ」ならテンプレをコピーして少し直すだけ。 今日はnix-shellとflakesの2つの作り方を、Node.jsの例で見ていこう!

なぜNixで開発環境を作るのか

「自分のPCでは動くのに、他の人の環境では動かない」——チーム開発で何度も繰り返されるこの問題の根本原因は、環境の再現性が保証されていないことにある。

brew install nodeapt install nodejsは「最新版を入れろ」という命令だ。先週インストールした人と今日インストールした人で、入るバージョンが違う可能性がある。READMEに「Node.js 18が必要」と書いてあっても、実際に18が入っている保証はどこにもない。

Nixはこの問題を、宣言型・純粋関数型の発想で解決する。詳しい設計思想は別記事「Nixとは?宣言型パッケージ管理で開発環境の「壊れた」をなくす方法」で解説しているが、ここで押さえておきたいのは次の3点だ。

  • バージョンが共存できる: Node.js 18と20を同じマシンに入れても衝突しない。プロジェクトごとに違うバージョンを使い分けられる。
  • 入力が同じなら結果も同じ: 設定ファイルが同じなら、いつ・どこで実行してもまったく同じ環境ができる。
  • システムを汚さない: 開発環境を作っても、グローバルな/usr/binを書き換えたりしない。シェルを抜ければ元通り。

つまりNixで開発環境を構築するということは、「環境の作り方」をコードとしてGitに残すということだ。新しいメンバーが入っても、PCを買い替えても、コマンド一発で同じ環境が手に入る。

この記事はNix本体がインストール済みであることを前提にしています。まだの場合は、Linux・macOSともに公式インストーラーのワンコマンドで導入できます。導入手順はNix公式ドキュメント(nixos.org)を参照してください。

nix-shellで一時的な環境を作る

まず一番手軽な使い方から。nix-shellを使うと、特定のツールが入った一時的なシェル環境をその場で立ち上げられる。システムには何もインストールされない。

たとえば「いまだけNode.jsを使いたい」場合。

# Node.jsが入った一時シェルに入る
nix-shell -p nodejs_18

# このシェルの中ではnodeが使える
node --version

# exitすると、元のシェルに戻る(nodeは消える)
exit

-p(package)オプションで、必要なパッケージを並べるだけだ。複数指定もできる。

# Node.js・jq・curlをまとめて使える一時環境
nix-shell -p nodejs_18 jq curl

シェルを抜けた瞬間、これらのツールは見えなくなる。グローバル環境を汚さずに「ちょっと試す」ができるのがnix-shell -pの良さだ。

似た用途のコマンドにnix runもあります。nix run nixpkgs#cowsay -- "Hello"のように、シェルに入らずワンショットでツールを実行できます。「一度だけ実行したい」ならnix run、「しばらくそのツールを触りたい」ならnix-shell -pが便利です。

ただしnix-shell -pは、その場限りの使い捨て向きだ。プロジェクトの開発環境として毎回同じツール群を呼び出すなら、次に紹介するshell.nixを使う方がはるかに管理しやすい。

shell.nixで再現可能な環境を定義する

プロジェクトに必要なツールをファイルに書き出すのがshell.nixだ。プロジェクトのルートに置き、nix-shellと打つだけで、そのプロジェクト専用の環境が立ち上がる。

# shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs_18
    python311
    git
    jq
  ];

  shellHook = ''
    echo "開発環境が準備できました"
    echo "Node.js: $(node --version)"
  '';
}
# プロジェクトディレクトリで実行するだけ
nix-shell

これでNode.js 18、Python 3.11、Git、jqが揃ったシェルが起動する。buildInputsに並べたものが、その環境で使えるツールだ。shellHookはシェルに入った瞬間に実行されるスクリプトで、環境変数の設定や起動メッセージの表示に使える。

ポイントは、このshell.nixGitリポジトリにコミットすることだ。チームの誰もがgit cloneしてnix-shellと打つだけで、まったく同じツールが揃った環境を手に入れられる。「README手順を1個ずつ実行する」必要はもうない。

direnvで自動化する

毎回nix-shellと打つのが面倒なら、direnvと組み合わせると快適になる。プロジェクトディレクトリに入るだけで、自動的にNix環境が有効になる仕組みだ。

# .envrcを作成して許可する
echo "use nix" > .envrc
direnv allow

これで対象ディレクトリにcdするたびに環境が自動ロードされ、ディレクトリを出ると元の環境に戻る。shell.nix.envrcをセットでコミットしておけば、クローンしてdirenv allowするだけで開発を始められる。

nix-shell + direnvは、Nixユーザーの間では定番のワークフローです。プロジェクトごとに異なるNode.jsやPythonのバージョンを使い分けたいときに特に威力を発揮します。

flakesによる現代的な環境管理

shell.nixには一つ弱点がある。冒頭のimport <nixpkgs> {}が指すnixpkgsのバージョンが環境によって違うかもしれない点だ。Aさんのマシンと、半年後に環境を作るBさんのマシンで、<nixpkgs>が別バージョンを指していれば、得られるNode.jsの細かいバージョンがズレる可能性がある。

これを解決するのが**Nix Flakes(フレイク)**だ。

Flakesは、依存しているnixpkgsのバージョンをflake.lockファイルで正確に固定する。npmのpackage-lock.jsonやRustのCargo.lockと同じ発想だと考えればわかりやすい。一度lockファイルができれば、いつ・どこで実行してもまったく同じ環境が再現される。

flake.nixの例

# flake.nix
{
  description = "My project development environment";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            nodejs_18
            python311
            git
          ];
        };
      }
    );
}

inputsで「どのnixpkgsを使うか」をGitのブランチやタグで指定し、devShells.defaultで開発環境の中身を定義している。flake-utilsを使っているのは、Linux(x86_64)でもmacOS(Apple Silicon)でも同じ定義が動くようにするためだ。

# Flakeベースの開発環境に入る
nix develop

# 初回実行時にflake.lockが自動生成される
git add flake.nix flake.lock

shell.nixではnix-shellだったが、Flakesではnix developで環境に入る。重要なのは生成されたflake.lock必ずGitにコミットすることだ。このファイルが依存関係の正確なバージョン(Gitのコミットハッシュ)を記録しており、再現性の要になる。

Flakesを有効にする

Flakesは便利だが、2026年現在もNixでは「実験的機能」という位置づけのため、明示的に有効化する必要がある。

# ~/.config/nix/nix.conf に追記する
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf

「実験的機能」とはいえ、Flakesは事実上のデファクトスタンダードになっています。新しくNixプロジェクトを始めるなら、最初からFlakesで構成しておくのがおすすめです。

shell.nix と flakes の使い分け

2つの方法を整理しておこう。

比較項目shell.nix(nix-shell)flake.nix(nix develop)
環境に入るコマンドnix-shellnix develop
nixpkgsのバージョン固定弱い(<nixpkgs>に依存)強い(flake.lockで固定)
必要な設定なし(すぐ使える)experimental-featuresの有効化
学習コスト低めやや高め
再現性中(環境により差が出うる)高(完全に固定)
向いている場面個人・小規模・お試しチーム・長期・厳密な再現

ざっくり言えば、まずshell.nixで試し、本格的にチームで使うならflakesという流れが自然だ。どちらもpkgs.mkShellbuildInputsという同じ部品を使うので、shell.nixに慣れていればFlakesへの移行は難しくない。

迷ったら最初はshell.nixで十分です。プロジェクトが育ってチームの人数が増え、「環境の細かいズレ」が問題になり始めたタイミングでflakesに移行すると、無理なくステップアップできます。

Node.js開発環境の実例

具体的にNode.jsプロジェクトの環境を組んでみよう。よくあるのは「特定のNode.jsバージョン + よく使うCLIツール」の組み合わせだ。

# shell.nix(Node.jsプロジェクト向け)
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs_18
    nodePackages.pnpm
    nodePackages.typescript
    git
  ];

  shellHook = ''
    echo "Node.js: $(node --version)"
    echo "pnpm:    $(pnpm --version)"
  '';
}

このshell.nixがあれば、nix-shellに入った時点でNode.js 18、pnpm、TypeScript、Gitがすべて使える状態になる。チームメンバーがNode.jsを手動でインストールする必要はない。

# 環境に入る
nix-shell

# あとは普段どおり
pnpm install
pnpm run dev

ここで一つ注意点がある。node_modulesの中身はNixが管理しないということだ。Nixが揃えるのは「Node.js本体やpnpmといったツール」であり、pnpm installで入れるプロジェクト依存パッケージは、これまでどおりpnpm(やnpm)が管理する。Nixは「土台となるツールチェーンの再現性」を担保する役割だと考えるとわかりやすい。

別バージョンが必要になったら、buildInputsnodejs_18nodejs_20に書き換えるだけでいい。

buildInputs = with pkgs; [
  nodejs_20   # 18から20に変えるだけ
  nodePackages.pnpm
];

書き換えてもう一度nix-shellに入れば、今度はNode.js 20の環境が立ち上がる。グローバルなNode.jsを入れ替える必要も、バージョン管理ツールを別途用意する必要もない。プロジェクトごとに違うバージョンを使い分けるのが、設定ファイル1行の変更で済む。

利用できるパッケージ名(nodejs_18python311など)や、特定バージョンの提供状況はnixpkgsの更新によって変わります。使いたいパッケージがあるかどうかはnix search nixpkgs nodejsのようにして確認するか、search.nixos.orgで検索してください。本記事のバージョン番号は一例です。

Dockerとの使い分け

「環境を固定するならDockerでいいのでは?」とよく聞かれる。結論から言うと、両者は競合ではなく補完関係だ。

観点Nix(nix-shell / flakes)Docker
主な対象開発環境のツールチェーンアプリの実行環境まるごと
分離のレベルホスト上で動く(軽量)コンテナで隔離
ホストとの親和性高い(ローカルのファイルをそのまま編集)ボリュームマウントが必要
本番環境との一致ツール単位環境まるごと一致させやすい
起動の速さ速い(ネイティブ実行)イメージ次第

使い分けの目安はこうだ。

  • 手元の開発作業をサクサク進めたいなら、ホスト上でネイティブに動くNixが軽快だ。エディタやデバッガとの相性もよい。
  • 本番と同じOS・ミドルウェア構成で動かしたい、あるいはPostgreSQLなどのミドルウェアごと隔離したいなら、Dockerが向く。

そして両者は組み合わせられる。NixでDockerイメージをビルドするという使い方も人気で、「開発はNix、配布はDockerイメージ」というワークフローも成り立つ。Dockerの代替や軽量化を検討しているなら、「Docker Desktopの代替ツール」もあわせて参考にしてほしい。

「ローカルの開発環境はNixで軽く、本番デプロイはコンテナで」という分業は、多くのチームで現実的な落としどころになっています。どちらか一方に絞る必要はありません。

よくある質問(FAQ)

Q.1

nix-shellとnix developの違いは何ですか?

先生
A.

どちらも開発環境のシェルに入るコマンドですが、対象が違います。nix-shellは伝統的なshell.nixを読み込み、nix developはFlakes(flake.nix)を読み込みます。nix developはflake.lockで依存バージョンを厳密に固定できるため、再現性がより高いのが特長です。まずはnix-shellで試し、チームで厳密に揃えたくなったらnix developへ移行するのがおすすめです。

Q.2

shell.nixとflake.nixはどちらを使うべきですか?

先生
A.

個人やお試しならshell.nixで十分です。設定なしですぐ使えて学習コストも低めです。一方、チームで長期的に開発し、環境のわずかなズレも許したくない場合はflakesが向きます。flake.lockによって誰の環境でも完全に同じものが再現されるためです。どちらもpkgs.mkShellという同じ部品を使うので、移行はスムーズです。

Q.3

Nixで作った環境でnpm installは普通に使えますか?

先生
A.

使えます。Nixが揃えるのはNode.js本体やpnpm・npmといった「ツールチェーン」で、npm installやpnpm installで入るプロジェクト依存パッケージはこれまでどおり各ツールが管理します。Nixは土台のツールの再現性を担保し、node_modulesの管理は従来どおり、という役割分担になります。

Q.4

Node.jsのバージョンを切り替えるにはどうすればいいですか?

先生
A.

shell.nixやflake.nixのbuildInputsに書いたパッケージ名を変えるだけです。たとえばnodejs_18nodejs_20に書き換えて、もう一度nix-shell(またはnix develop)に入り直せば、新しいバージョンの環境が立ち上がります。グローバルなNode.jsを入れ替えたり、別途バージョン管理ツールを用意したりする必要はありません。

Q.5

Dockerがあれば、Nixで開発環境を作る必要はないのでは?

先生
A.

両者は補完関係です。Dockerはアプリの実行環境まるごとを隔離するのが得意で、Nixはホスト上で動く軽量なツールチェーン管理が得意です。手元の開発を軽快に進めたいならNix、本番と同じ構成やミドルウェアごと隔離したいならDocker、という使い分けが現実的です。NixでDockerイメージをビルドするなど、両方を組み合わせる使い方も人気です。

まとめ:まずはshell.nixを1つ置いてみる

Nixで開発環境を構築する流れを振り返っておこう。

  • nix-shell -p で、その場限りのツールをサッと使える。「ちょっと試す」用途に最適。
  • shell.nix に必要なツールを書いてコミットすれば、チーム全員がnix-shell一発で同じ環境を再現できる。
  • direnv と組み合わせれば、ディレクトリに入るだけで環境が自動で有効になる。
  • flakes(flake.nix) ならflake.lockで依存を完全に固定でき、最も再現性が高い。
  • Node.jsのような環境も、バージョン変更は設定ファイル1行の書き換えで済む。
  • Dockerとは競合せず、開発はNix・配布はDockerのように補完し合える。

最初の一歩として難しいことは要らない。手元のプロジェクトにshell.nixを1つ置き、必要なツールを並べてnix-shellと打つ——それだけで「自分のPCでは動くのに」問題の多くから解放される。

いきなりflakesやNixOSを目指す必要はありません。まずはshell.nixで再現性の手応えをつかみ、気に入ったらflakesへステップアップしましょう。Nixの設計思想そのものをもっと知りたい人は「Nixとは?宣言型パッケージ管理」の記事もあわせてどうぞ。

おすすめコース

関連記事