0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PR: アディクシィ株式会社
ADiXiのコーポレートサイト

4つのプラットフォームを1つのNix Flakeで管理する設計 — 「適用範囲」と「管理対象」の2軸で設定を整理する

0
Last updated at Posted at 2026-09-16

Images in this article are AI-generated for illustrative purposes.

thumbnail

Nix FlakeとHome Managerを使い、macOS、NixOS、WSL、Nix-on-Droidの設定を1つのdotfilesリポジトリで管理する構成を題材として、「どの環境で使うか」という4層の適用範囲と、「何を管理するか」というシステム設定・Home Managerのユーザー設定の区分の2軸で設定を分類し、どこまで共通化し、どこから分離すべきかを整理した記事です。

本記事のターゲット

本記事は、以下の方をターゲットに執筆しました。

  • Nix FlakeやHome Managerを使い始めた方
  • 複数のOSや端末でdotfilesを共有したい方
  • flake.nixhome.nixが大きくなり、設定の置き場所に迷っている方
  • macOS、NixOS、WSLなどの設定をどこまで共通化すべきか知りたい方

はじめに

私はmacOS、NixOS、WSL上のNixOS、Android上のNix-on-Droidを使い、それぞれの開発環境を1つのdotfilesリポジトリで管理しています。

Nixを使い始めた当初は、「同じ設定をどの端末でも再現できるなら、できるだけ共通化した方がよい」と考えていました。

しかし、プラットフォームごとに異なる設定や依存関係を扱うようになると、単純な共通化では次のような問題が発生しました。

複数プラットフォームをNixで管理する上で起きた問題

複数のプラットフォームを1つのFlakeで管理すると、共通設定を再利用しやすくなります。一方、OSや端末ごとの差まで同じ設定にまとめると、別の環境で設定を読み込めなくなったり、設定の置き場所や責務が分かりにくくなったりします。

以下は、そういったNixの設定操作により起きた問題を表した一覧です。

macOSでしか使えないオプションやパッケージをNixOS側の構成に含める
  → ❌ プラットフォーム間の互換性がなく評価に失敗する

通常のNixOSとNix-on-Droidで`nixpkgs`の入力を共通化する
  → ❌ プラットフォーム固有の互換性に合わせて依存関係を更新できなくなる

USB/IPで自動接続するUSBポートのBUSIDや、GPUのPCIバスIDを共通モジュールに記述する
  → ❌ ホスト固有のハードウェア識別子が共通設定に固定され、別のホストへ再利用できなくなる

WSL固有のWindows連携やUSB/IPを通常のNixOS設定と同じ場所に記述する
  → ⚠️ OS・実行環境ごとの責務が曖昧になる

OS全体の設定とHome Managerのユーザー設定を同じ基準で配置する
  → ⚠️ 設定を管理・反映する主体が分かりにくくなる

上記の一覧は、「設定操作の例 → その操作によって起きた問題」の形式で整理しています。

上記の問題に共通するのは、設定を共有する範囲や管理する対象を区別しないまま、1つの構成へまとめている点です。

問題を避けるには、設定ごとに「どの環境で使うか」と「何を管理するか」を判断し、役割に合うファイルへ分ける必要があります。

本記事では、実際に運用しているdotfilesリポジトリを題材に、設定の適用範囲と管理対象を切り分け、複数の環境へ組み合わせる設計を整理します。

本記事の読み進め方

本記事では、全端末で使うGitの共通設定と、Macだけで使うDockの固有設定などを例に、分けた設定を端末ごとに組み合わせる設計を説明します。

Nixの各仕組みをまだ整理できていない方でも読み進められるよう、第1章ではNixと関連ツールの役割から説明します。その後、設定の分類、端末ごとの組み立て、新しい設定の配置判断、変更後の確認へと進みます。コード例では、属性セットや関数、withinheritなど、Nix言語の基本的な構文を使用します。

この章で分かること
1. Nixで何を管理できるかを知る Nix、NixOS、Home Managerはそれぞれ何をするのか。ソフトウェアの導入、OS全体の設定、個人の作業環境の設定を、どの仕組みが担当するかを確認します。
2. 設定を共有する範囲と管理する対象を分ける Gitの設定とmacOSのDock設定を、同じ場所に置いてよいのか。「どの環境で使うか」と「ユーザー環境・システムのどちらを管理するか」を組み合わせ、配置を考えます。
3. 分けた設定を端末ごとに組み合わせる 別々のファイルにした設定を、どうやって1台分の構成にするのか。入口となるflake.nixで設定を選び、ユーザー名などの必要な値を渡す流れを確認します。
4. 新しい設定の置き場所を決める ツールや端末を追加するとき、どこへ設定を書けばよいのか。ここまでの実例を判断手順にまとめ、共通化によって別の環境に問題が起きるケースも確認します。
5. 変更が各環境で使えるか確認する Macで問題なく使えた変更が、NixOSでも使えるとは限りません。設定を読み取って確認する段階と、実際の端末へ反映する段階を分け、最後に記事全体のファイル配置を振り返ります。

各章の番号は、本文の見出しと対応しています。読み終えたときには、「この設定をどの環境で共有するか」「どのファイルへ置くか」「変更後に何を確認するか」 を判断できるようになります。

あわせて、本記事の題材であるdotfilesリポジトリについて、各ディレクトリの役割と、分割した設定を4つの環境へ組み合わせる流れを理解できることを目指します。

1. Nixで何を管理できるかを知る

開発環境を構築する際には、ソフトウェアのインストールだけでなく、シェルの設定やOSのサービス設定など、さまざまな環境設定が必要になります。

Nixでは、こうした環境設定は「設定ファイル」として記述されており、Nixを中心に、Nix FlakeやHome Managerなどの関連ツールを組み合わせて管理されています。

まず1.1節では、Nixと各関連ツールが「それぞれ何を担当しているのか」を整理します。続く1.2節では、本記事で扱う4つの環境について、「どのツールを組み合わせて管理しているのか」を確認します。

それぞれの役割と使い分けを理解しておくことで、後の章でも「この設定はどの仕組みで管理されているのか」を捉えやすくなります。

1.1. 使用しているNixの仕組み

Nixは、ソフトウェアとその依存関係を再現可能な形で管理するパッケージマネージャーであり、その構成を記述するためのNix言語も提供しています。

一般的なパッケージマネージャーでは、コマンドを実行してソフトウェアをインストールします。一方、Nixでは「Gitやzshを利用する」「この設定値を適用する」といった 実現したい状態を設定ファイル(configuration.nixflake.nixなど)に記述し、その内容をもとに環境を構築できます

さらに、インストールするソフトウェアだけでなく、ユーザー、ネットワーク、サービスなどのシステム設定もNix言語で記述できます。こうしたOSの状態をコードとして記述し、その設定を適用することで、必要なソフトウェアやシステム設定を別の端末にも構築できる考え方 は、Infrastructure as Code(IaC)になぞらえて 「OS as Code」 と呼ばれます。

Nix自体はNixOS専用ではなく、macOSや他のLinuxディストリビューションでも利用できます。

また、Nixは「どこまで管理を任せるか」も柔軟に選択できるため、必ずしも環境全体をNixで管理する必要はありません。「まずはソフトウェアの管理だけに利用し、必要に応じてシェルや設定ファイルなどのユーザー環境、さらにOSの設定へと...」といったように後から管理範囲を拡張することもできます

このように、利用するOSや管理したい範囲に応じて、Nixpkgs、Home Manager、NixOS、nix-darwinなどの仕組みを組み合わせられる点も、Nixの特徴のひとつです。

次の表では、Nixを土台として、パッケージの定義を提供する仕組み、依存関係と構成の入口を管理する仕組み、OSやユーザー環境へ設定を適用する仕組みに分けて、それぞれの役割を整理します。

これらは個別に選ぶものではなく、管理するOSや設定の範囲に応じて組み合わせて利用します。

仕組み 主な役割
Nix ソフトウェアとその依存関係を管理し、Nix言語で書かれた構成を読み取る
Nixpkgs Gitやzshなど、多くのソフトウェアをNixで利用するためのパッケージ定義を提供する
Nix Flake 外部から利用する依存関係と、それらを使って提供するパッケージや環境の構成を、共通の形式で定義する
NixOS NixOSのシステム全体を宣言的に管理する
Home Manager シェル、CLI、設定ファイルなど、ユーザー環境を管理する
nix-darwin macOSのシステム設定をNixのモジュールとして管理する
NixOS-WSL WSL上でNixOSを動かすための設定を提供する
Nix-on-Droid Android上でNixパッケージと独自のモジュール構成を利用する

まずは、依存関係を入力として固定し、構成を出力として公開するFlakeから見ていきましょう。

flake.nixflake.lockの役割

Flakeの入口になるファイルがflake.nixです。ここには、外部から利用するNixpkgsやHome Managerなどをinputs(入力)、それらを使って提供するパッケージや各端末の構成などをoutputs(出力)として定義します。依存関係のどのバージョンを使うかは、flake.lockにリビジョン(特定時点のバージョン)として記録されます。

Flakesは広く利用されていますが、Nix公式ドキュメント上では現在も実験的機能として案内されています。本記事は、現在提供されているFlakeのインターフェースを使用した構成例としてご覧ください。

モジュールとimportsの役割

設定を役割ごとに分ける単位が、モジュールです。モジュールには設定項目(オプション)の値などを書き、importsで別のモジュールを読み込んで組み合わせます。例えば、シェルの設定とGitの設定を別々のファイルで管理できます。

Flakeやモジュールは、Nix式として記述されています。Nixはこれらの式を読み取り、各モジュールの設定を組み合わせて、端末へ適用する構成を表す値を求めます。このように、Nix式を値へ変換する処理を「評価」と呼びます。

評価は、設定を実際の端末へ反映する処理ではありません。評価で得られた構成をもとに、必要なソフトウェアや設定ファイルをビルドし、生成した構成を端末へ反映します。全体の流れは「評価→ビルド→反映」と捉えると分かりやすいでしょう。

第5章では、各段階で設定を確認し、実際の端末へ反映する方法を説明します。

1.2. 管理している環境

今回の構成では、次の4環境を管理しています。

環境 アーキテクチャ Flake出力 主な用途
macOS aarch64-darwin darwinConfigurations メインのデスクトップ環境
NixOS x86_64-linux nixosConfigurations.nixos NixOSデスクトップ環境
NixOS on WSL x86_64-linux nixosConfigurations.windows-vm Windows上のAndroid開発環境
Nix-on-Droid aarch64-linux nixOnDroidConfigurations.pixel7pro Android端末上のCLI環境

すべてNixを利用していますが、同じ種類の環境ではありません。各環境で使用する仕組みの違いを、以下のように区別しています。

  • macOSでは、nix-darwinとHome Managerを使用する
  • NixOSでは、NixOSモジュールによるシステム設定とHome Managerを使用する
  • WSLでは、NixOSにNixOS-WSL固有のモジュールを追加する
  • Androidでは、NixOSではなくNix-on-Droid独自のモジュールを使用する

ここを区別せずに共通化すると、システム設定とユーザー設定、OS固有設定と端末固有設定が混ざりやすくなるため、注意が必要です。

ここまで、Nixで利用する仕組みと、管理対象となる4つの環境を確認しました。

2. 設定を共有する範囲と管理する対象を分ける

ここからは、冒頭で挙げた問題を避けるための設計を、実際のリポジトリ構成に沿って説明します。

dotfilesのリポジトリ直下は、次のような構成になっています。

dotfilesリポジトリ構成の全体像

dotfiles
├── flake.nix                  # 各環境のモジュールを接続
├── config                     # 複数モジュールで共有する公開可能な値
├── home                       # Home Managerで扱うユーザー設定
│   ├── base                   # 全環境共通
│   ├── nixos                  # NixOS系共通
│   └── darwin                 # macOS固有
├── lib                        # 複数モジュールで共有する生成処理
├── modules                    # システム・実行環境の設定
│   ├── nixos                  # NixOS系共通
│   ├── darwin                 # macOS固有
│   ├── wsl                    # WSL固有
│   └── nix-on-droid           # Nix-on-Droid固有
└── hosts                      # ホスト固有設定
    ├── Mac-mini
    ├── xps15
    ├── windows-vm
    └── pixel7pro

このツリーを全体像として、「配置先を決める2つの観点」を順に見ていきます。

配置先を決める2つの観点

1つ目は、「どの環境で使うか」という適用範囲です。

例えば、Gitの設定は複数の端末で共有できますが、macOSのDock設定はmacOSでしか使いません。

2.1節では、この適用範囲を全環境共通・OS系統共通・プラットフォーム固有・ホスト固有の4層に分けます。ここでは、OSであるmacOS、WSLという実行基盤、Android上のNix-on-Droidを、それぞれ異なる構成の仕組みを持つ「プラットフォーム」として扱います。

ホストとは、設定を適用する個別のPC、仮想マシン、スマートフォンなどを指します。

2つ目は、「何を管理するか」という管理対象です。

例えば、同じNixOS向けの設定でも、OSのユーザーを作る設定と、そのユーザーが使うGitの設定では、管理する対象が異なります。

2.2節では、Home Managerで扱うユーザー設定をhome、各環境を直接管理する設定をmodulesへ配置する考え方を整理します。全環境で共有するユーザー設定はhome/base、macOSだけで使うシステム設定はmodules/darwinというように、2つの観点を組み合わせて配置先を決めます。

homemodulesは、このリポジトリで設定を分類するために付けたディレクトリ名であり、Nixで特別な意味を持つ名前ではありません。設定を有効にするには、後述の第3章にあるように各端末の構成から対象のモジュールを読み込む必要があります。

2.1. 設定を4つの適用範囲に分ける

今回の構成では、設定を適用する環境の広さに応じて、次の4層に分けています。上の層ほど多くの環境で共有し、下の層ほど特定のプラットフォームやホストに限定します。

🌐 適用範囲が広い 🌐

↑

① 全環境共通
   └── zsh、Git、ripgrep、共通エイリアス

② OS系統共通
   └── 実機のNixOSとWSL上のNixOSで共有するロケール、フォント、Docker、ユーザー設定

③ プラットフォーム固有
   └── macOSのDock、WSLのWindows連携、Nix-on-DroidのAndroid連携

④ ホスト固有
   └── ホスト名、USB機器のBUSID、GPUドライバー、端末のフォント

↓

📍 適用範囲が狭い 📍

この4層をもとに設定の配置先を決める際は、最初からファイル名を考えるのではなく、「どの環境に適用される設定なのか」を先に考えます。

新しい設定の適用範囲と配置先を判断するために、①〜④の例を見てみましょう。

① 全環境共通

全環境で同じように使用するユーザー設定は、home/baseへ配置します。

home/base
├── default.nix
└── programs
    ├── git
    ├── zsh
    ├── lazygit
    ├── lazyvim
    ├── mise
    └── android

home/base/default.nixでは、共通で使用するCLIをインストールし、ツールごとのモジュールをインポートしています。

{ lib, pkgs, unstable, ... }:

{
  # ...

  # ツールごとに分割したHome Managerモジュールを読み込む
  imports = [
    ./programs/zsh
    ./programs/ssh
    ./programs/git
    ./programs/lazyvim
    ./programs/lazygit
    ./programs/android
    ./programs/mise
  ];

  # OSを問わず使用するCLIを全環境へ導入する
  home.packages =
    (with pkgs; [
      ripgrep
      fd
      jq
      curl
      git-lfs
      nixfmt
      statix
    ])
    # クリップボード用CLIはLinux環境にだけ追加する
    ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [
      pkgs.wl-clipboard
      pkgs.xclip
    ];

  # Home Manager自身もHome Managerで管理する
  programs.home-manager.enable = true;

  # ...
}

全環境共通のユーザー設定を扱うhome/baseには、「現在すべての環境で必要な設定」を置きます。ただし、配置を決めるうえで1つ注意すべき点があります。

それは「設定ファイルを共有したいという理由だけで、すべてをhome/baseへ置くわけではない」という点です。

例えばGUIアプリの起動方法やOSサービスは環境ごとの差が大きいため、基本ルールとして共通層には置かないようにします。

共通層で条件分岐してよいケース

共通設定の中でも、パッケージの対応プラットフォームだけが異なるケースがあります。

例えば、Linuxでのみクリップボード用パッケージを追加する程度であれば、lib.optionalsを使用した小さな分岐で対応できます。

home.packages = commonPackages
  # Linuxで評価するときだけ、次のパッケージをリストへ追加する
  ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [
    pkgs.wl-clipboard
    pkgs.xclip
  ];

また、パッケージが現在のプラットフォームで利用できるか確認する場合は、lib.meta.availableOnを使用できます。

home.packages =
  # adbは全環境で使用する
  [ pkgs.android-tools ]
  # 対象プラットフォームで利用できる場合だけAndroid CLIを追加する
  ++ lib.optional
    (lib.meta.availableOn pkgs.stdenv.hostPlatform unstable.android-cli)
    unstable.android-cli;

条件分岐が1〜2か所で、設定の目的自体が全環境で共通している場合は、共通モジュール内に残しても把握しやすいと考えています。

プラットフォーム固有のモジュールへ分けるケース

一方、環境ごとの差がパッケージの有無だけでなく、サービスや起動処理にまで広がる場合は、共通モジュールからプラットフォーム固有のモジュールへ分けます。

ただし、設定を別々のモジュールへ配置しても、共通する生成処理まで重複させる必要はありません。現在の構成では、NixOS系のHome Manager設定とWSLのシステム設定の両方で使うAndroid Studioの起動処理を、純粋なNix関数としてlib/android-studio.nixへ抽出しています。

# ...

# 共通関数へ、環境ごとに異なるパッケージとWSL判定を渡す
studioSupport = import ../../lib/android-studio.nix {
  inherit lib pkgs isWsl;
  androidStudio = unstable.android-studio;
};

# 共通関数から、この環境で使う起動コマンドを生成する
studio = studioSupport.mkLauncher "studio";

# ...

homemodulesは管理対象によって分けつつ、同じ値やスクリプトを生成するロジックだけをlibで共有します。これにより、システム設定とHome Manager設定を同じモジュールへ混ぜずに重複を減らせます。

② OS系統共通

複数の実行環境が同じOS系統を共有する場合、その共通部分を中間層へ配置します。

今回の構成では、通常のNixOSとWSL上のNixOSで次の設定を共有しています。

  • ロケールとタイムゾーン
  • フォント
  • Linuxユーザー
  • Docker
  • OpenSSH
  • Nixのガベージコレクション(GC)と最適化
  • NixOS系のHome Manager設定

本記事のWSL環境では、Ubuntuなどの一般的なLinuxディストリビューションではなく、NixOS-WSLを使ってWSL上でNixOSを動かしています。そのため、実機のNixOSとWSL上のNixOSは、ロケール、フォント、Dockerなどのシステム設定に加え、パッケージやGUIアプリなどのNixOS系のHome Manager設定も共有できます。

これらの共通設定は、管理対象に応じてシステム設定とHome Manager設定に分け、次のファイルへ配置しています。

OS系統共通
├── modules/nixos/common.nix  # 実機とWSL上のNixOSで共有するシステム設定
└── home/nixos/default.nix    # 両環境で共有するHome Manager設定

このように管理対象を分けることで、同じNixOS向けであっても、システム設定とユーザー設定の責務が混ざりません。

NixOS系の設定
├── システム設定
│   ├── users.users
│   ├── services.openssh
│   ├── virtualisation.docker
│   └── fonts.packages
│
└── ユーザー設定
    ├── home.packages
    ├── home.sessionVariables
    ├── programs.obsidian
    └── services.syncthing

modules側の設定は、NixOS、nix-darwin、Nix-on-Droidそれぞれのモジュールシステムが管理する環境を対象とします。NixOSとnix-darwinでシステム構成を反映する場合は、通常、管理者権限が必要です。

一方、Nix-on-DroidはAndroidのroot権限を必要とせず、アプリ内の環境を独自のモジュールで管理します。ユーザー設定は、各ユーザーのホームディレクトリやユーザー単位のパッケージを対象とし、Home Managerで管理します。

同じNixOSで使う設定でも、OS全体を管理するものと、各ユーザーの環境を管理するものでは、配置先を分ける必要があります。

③ プラットフォーム固有

macOS、WSL、Nix-on-Droidのように、その環境の仕組みに依存する環境側の設定は、modules配下の専用モジュールへ配置します。Home Managerにも環境固有の設定がある場合は、home配下に対応するエントリーポイントを用意します。

プラットフォーム固有
├── modules
│   ├── darwin          # macOSのシステム設定
│   ├── wsl             # WSL固有のシステム設定
│   └── nix-on-droid    # Nix-on-Droid環境の設定
└── home
    └── darwin          # macOS固有のHome Manager設定
macOS固有設定

macOSでは、nix-darwinを使ってDock、Finder、キーボード、launchdなどを管理しています。また、Nixpkgsだけで管理しにくいGUIアプリは、Homebrewを利用してインストールしています。

この構成では、Homebrew本体をnix-homebrewで管理しています。nix-homebrewは、Homebrewのインストールとバージョン固定をnix-darwinの設定に組み込むためのモジュールです。

Homebrew本体のソースコードはnix-homebrewのFlakeの入力として固定され、NixによってダウンロードされてNixストアへ保存されます。nix-homebrewは、Nixストア内のコードを標準のHomebrew配置から利用できるように設定するため、公式のインストールスクリプトを別途実行する必要はありません。

ただし、nix-homebrewが管理するのはHomebrew本体であり、Homebrewでインストールするパッケージの一覧ではありません。

flake.nixでは、nix-homebrewのモジュールをmacOSの構成へ追加し、Homebrewを有効にしています。

# flake.nix
modules = [
  # ...

  # Homebrew本体を管理するnix-homebrewモジュールをmacOS出力へ追加する
  nix-homebrew.darwinModules.nix-homebrew
  {
    nix-homebrew = {
      # Apple Silicon側とRosetta側のHomebrewを同じユーザーで管理する
      enable = true;
      enableRosetta = true;
      user = username;
    };
  }

  # ...
];

実際にインストールするパッケージは、nix-darwinのhomebrew.brewshomebrew.casksへ記述します。brewsには主にCLIツールなどのパッケージを、casksにはGUIアプリなどを指定します。

# modules/darwin/default.nix
# ...

{
  # FinderとDockのmacOS既定値を宣言する
  system.defaults = {
    finder = {
      AppleShowAllExtensions = true;
      ShowPathbar = true;
      ShowStatusBar = true;
    };

    dock = {
      autohide = true;
      show-recents = false;
      orientation = "bottom";
    };
  };

  # GUIアプリはHomebrew経由でインストールする
  homebrew = {
    enable = true;
    casks = [
      "ghostty"
      "google-chrome"
      "karabiner-elements"
      "obsidian"
    ];
  };

  # ...
}

nix-darwinの構成を反映すると、ここで宣言したGUIアプリがHomebrewによってインストールされます。これにより、Homebrew本体の導入と、Homebrewでインストールするパッケージの一覧をNixの設定へまとめられます。

ここまで見てきたmacOS固有の設定も、管理する対象によって配置先を分けます。

DockやFinderの設定を表すsystem.defaultsと、バックグラウンドサービスを管理するlaunchdは、macOSのシステム設定です。そのため、modules/darwinへ配置します。

一方、Karabiner-Elementsは、アプリ本体をHomebrewでインストールし、キー割り当てなどのユーザー設定をHome Managerで管理しています。ただし、この設定では入力ソースの切り替えにmacOS専用のCLIツールmacismを使用するため、ほかのOSとは共有できません。そこで、全環境共通のhome/baseではなく、macOSでだけ読み込むhome/darwinへ配置します。

# home/darwin/default.nix
{ ... }:

{
  # 共通ユーザー設定へ、macOS専用のKarabiner設定を重ねる
  imports = [
    ../base                 # 全環境共通のユーザー設定
    ./programs/karabiner    # macOS固有のユーザー設定
  ];

  # ...
}

以前は、Karabinerの設定をhome/baseから読み込んでいました。そのため、NixOSやWSLの構成にもmacOS専用のmacismが含まれ、対応していないプラットフォームとして評価に失敗しました。この例から、ユーザー設定であっても、すべての環境で共有できるとは限らないことが分かります。

macismは、macOSの入力ソースをコマンドラインから取得・切り替えるツールです。この構成では、Karabiner-Elementsで右Shiftを単独押ししたときにmacismを呼び出し、日本語入力とABCを切り替えています。

WSL固有設定

WSLでは、Windowsとの連携やUSB/IP、GUIアプリの起動方法など、通常のNixOSには不要な設定が発生します。

# ...

{
  # 実機NixOSと共有するシステム設定を土台にする
  imports = [ ../nixos/common.nix ];

  # 一般的なLinux向けバイナリをNixOS上で実行可能にする
  programs.nix-ld.enable = true;

  # USB/IPで接続したAndroid端末を一般ユーザーから操作できるようにする
  services.udev.extraRules = ''
    SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE:="0666", TAG+="uaccess"
  '';

  environment = {
    # WSLとWindowsの連携に必要なパッケージとラッパーを導入する
    systemPackages = with pkgs; [
      kmod
      usbutils
      android-tools
      androidStudioWsl
      wslOpen
    ];

    # URLやファイルをWindows側の既定アプリで開く
    variables.BROWSER = "wsl-open";
  };

  # ...
}

このモジュールはNixOS共通のmodules/nixos/common.nixを読み込んだ上で、WSLに必要な差分だけを追加しています。

NixOS共通設定
    ↓ 読み込む
WSL固有設定を追加
    ↓ 読み込む
windows-vm固有設定を追加

差分を積み上げる構成にすることで、NixOS共通設定を複製せず、WSLの特殊な処理も共通層へ漏らさずに管理できます。

Nix-on-Droid固有設定

Nix-on-DroidはAndroid上でNixを利用できますが、NixOSそのものではありません。そのため、NixOSのシステム設定をそのまま共有せず、Nix-on-Droid用のモジュールからHome Managerの共通設定を読み込みます。

# ...

{
  # Nix-on-Droid内で直接使う最小限のCLIを導入する
  environment.packages = with pkgs; [
    coreutils
    curl
    git
    openssh
    vim
  ];

  # Android端末でも共通のzsh設定を使う
  user.shell = "${pkgs.zsh}/bin/zsh";

  home-manager = {
    # Nix-on-DroidのパッケージセットをHome Managerでも共有する
    useGlobalPkgs = true;
    # NixOSのシステム設定ではなく、全環境共通のユーザー設定だけを読み込む
    config.imports = [ ../../home/base ];
  };

  # ...
}

共有しているのは、zshやGitなどのユーザー環境です。NixOSのDocker、systemd、fontconfigなどのシステム設定は共有していません。

同じNixモジュール形式に見えても、評価するモジュールシステムが異なる場合があります。「Nixで書かれているか」ではなく、「どのモジュールシステムが評価するか」で分ける必要があります。

④ ホスト固有

特定の端末、ハードウェア、ホスト名に依存する設定はhostsへ配置します。

hosts
├── Mac-mini
├── xps15
├── windows-vm
└── pixel7pro

例えば、WSLのホスト設定には次のような値があります。

{ username, ... }:

{
  # WSL共通設定へ、この仮想マシン固有の値を重ねる
  imports = [ ../../modules/wsl ];

  wsl = {
    enable = true;
    defaultUser = username;
    usbip = {
      # USB/IP機能自体はWSL共通だが、BUSIDは接続先PC固有の値
      enable = true;
      autoAttach = [ "4-7" ];
    };
  };

  # NixOSとWindows側のWSL設定で同じホスト名を使う
  networking.hostName = "windows-vm";

  # ...
}

wsl.usbip.enableはWSLという実行環境で共通利用できる仕組みです。一方、autoAttach = [ "4-7" ]のBUSIDは、現在接続しているPCとUSBポートに依存します。

プラットフォーム固有とホスト固有の違いを、WSLのUSB/IP設定を例に確認します。

設定 適用範囲 配置先
USB/IP機能を利用する プラットフォーム固有 modules/wsl
自動接続するBUSID ホスト固有 hosts/windows-vm

USB/IP機能のようにWSL環境で共有できる設定はmodules/wslへ、BUSIDのように特定の端末へ依存する設定はhosts/windows-vmへ配置します。

ホスト固有値を共通モジュールへ置かないことで、同じ実行環境の端末を追加するときに既存設定を流用しやすくなります。

2.2. システム設定とHome Managerのユーザー設定に分ける

4層は「どの環境へ適用するか」を表す分類です。実際のファイル配置では、これとは別に「何を管理するか」を判断します。

システム設定とHome Managerのユーザー設定の分け方は、利用するOSや運用方針によって異なります。ここで示す区分はNix共通のルールではなく、今回の構成で採用している設計例です。

各OSや実行環境を直接管理するシステム設定と、Home Managerで扱うユーザー設定の違いを整理すると、次のようになります。

比較項目 システム設定 Home Managerのユーザー設定
管理する対象 各モジュールシステムが管理する環境、サービス、ユーザー、ハードウェア 各ユーザーのパッケージ、設定ファイル、ユーザー単位のサービス
主な仕組み NixOS、nix-darwin、NixOS-WSL、Nix-on-Droidのモジュール Home Manager
設定例 ロケール、Docker、OpenSSH、Dock、Fcitx5の有効化 Git、Android Studio、Obsidian、Karabiner、Fcitx5のユーザー設定
主な配置先 modules home

設定の種類だけで配置先が決まるわけではありません。同じパッケージやサービスでも、OS全体で管理するのか、各ユーザーの環境で管理するのかによって配置先が変わります。

例えば、実機のNixOSとWSLで共有する設定にも、次の2種類があります。

  • ロケール、ユーザー、フォント、Docker、OpenSSHなどのシステム設定は、OS全体へ反映するためmodules/nixosへ配置する
  • Android Studio、Obsidian、Syncthing、Fcitx5のプロファイルなどのユーザー設定は、Home Managerでユーザー環境へ反映するためhome/nixosへ配置する

Fcitx5のように1つの機能が両方の管理対象にまたがる場合もあります。この構成では、入力メソッドの有効化はNixOSやWSLのシステム設定へ、ユーザーが使うパッケージ、環境変数、プロファイルはHome Manager設定へ分けています。機能名だけで配置先を決めず、実際に管理する状態ごとに分けることが重要です。

2.1節の適用範囲と、ここで説明した管理対象を組み合わせると、配置先は次のように決まります。

設定 適用範囲 管理対象 配置先
Git 全環境共通 ユーザー設定 home/base
Docker OS系統共通 システム設定 modules/nixos
Fcitx5の有効化(XPS 15) ホスト固有 システム設定 hosts/xps15
Fcitx5の有効化(WSL) プラットフォーム固有 システム設定 modules/wsl
Fcitx5のユーザー設定 OS系統共通 ユーザー設定 home/nixos
Dock プラットフォーム固有 システム設定 modules/darwin
Karabinerの設定 プラットフォーム固有 ユーザー設定 home/darwin
USB/IP機能 プラットフォーム固有 システム設定 modules/wsl

このように、「どの環境で使うか」だけでなく「何を管理するか」まで確認することで、同じ適用範囲の設定でもシステム設定とユーザー設定を分けて配置できます。

3. 分けた設定を端末ごとに組み合わせる

第2章では、設定の置き場所を分けました。

次に必要なのは、「この端末では、どの設定を読み込むか」を決めることです。例えば、Macには全環境共通のユーザー設定とmacOS固有の設定を、WSLにはNixOS共通の設定とWindows連携の設定を組み合わせます。

3.1節では、入口となるflake.nixから各端末の設定を選び、分割したモジュールをたどって1台分の構成を組み立てます。この構成が、Flakeのoutputsで公開する出力の1つになります。

また、複数のモジュールで同じユーザー名などを使う場合は、その値を渡す仕組みも必要です。3.2節では、値の受け渡しに使うspecialArgsextraSpecialArgsを説明します。ここでは「どの設定を読み込むか」と「その設定へ何を渡すか」の流れを押さえます。

3.1. flake.nixは各層を接続する

ここまで分けたモジュールを、最後にflake.nixで接続します。

flake.nix
├── nixosConfigurations.nixos                 # ❄️ NixOS実機向けの出力
│   └── hosts/xps15                           # XPS 15固有の設定
│       └── modules/nixos                     # NixOS系で共有するシステム設定
│           └── home/nixos → home/base        # NixOS系から全環境共通のユーザー設定を読み込む
├── nixosConfigurations.windows-vm            # 🟦 WSL向けの出力
│   ├── nixos-wsl                             # NixOS-WSLが提供するモジュール
│   └── hosts/windows-vm                      # WSLホスト固有の設定
│       └── modules/wsl → modules/nixos       # WSL固有設定からNixOS共通設定を読み込む
│                         └── home/nixos → home/base # NixOS系から全環境共通のユーザー設定を読み込む
├── darwinConfigurations.<host>               # 🍎 macOS向けの出力
│   ├── hosts/Mac-mini                        # Mac mini固有の設定
│   │   └── home/darwin → home/base           # macOSから全環境共通のユーザー設定を読み込む
│   └── modules/darwin                        # macOS固有のシステム設定
└── nixOnDroidConfigurations.pixel7pro        # 🟢 Nix-on-Droid向けの出力
    └── hosts/pixel7pro                       # Pixel 7 Pro固有の設定
        └── modules/nix-on-droid              # Nix-on-Droid固有の設定
            └── home/base                     # 全環境共通のユーザー設定を読み込む

NixOSとWSLの出力は、次のように構成しています。

let
  # ...

  # NixOS実機とWSLに共通する構成生成処理
  mkNixosConfiguration =
    {
      # 端末固有の設定を呼び出し側から受け取る
      hostModule,
      # WSLだけで必要なHome Managerの差分を判定する
      isWsl ? false,
      # NixOS-WSLなど、環境固有の追加モジュールを受け取る
      platformModules ? [ ],
    }:
    nixpkgs.lib.nixosSystem {
      # NixOS実機とWSLは同じCPU・OS向けパッケージセットを使う
      system = systems.linux;
      # NixOSモジュールで共有する値を渡す
      specialArgs = nixosSpecialArgs;
      modules = platformModules ++ [
        hostModule
        # NixOS構成へHome Managerを組み込む
        home-manager.nixosModules.home-manager
        {
          home-manager = {
            useGlobalPkgs = true;
            useUserPackages = true;
            # Home Managerモジュールで共有する値とWSL判定を渡す
            extraSpecialArgs = mkHomeManagerExtraArgs {
              unstablePackages = nixosUnstable;
              inherit isWsl;
            };
          };
        }
      ];
    };
in
{
  nixosConfigurations = {
    # 実機NixOSはXPS 15のホスト設定だけを渡す
    nixos = mkNixosConfiguration {
      hostModule = ./hosts/xps15/configuration.nix;
    };

    # WSLはホスト設定に加え、WSL判定とNixOS-WSLモジュールを渡す
    windows-vm = mkNixosConfiguration {
      hostModule = ./hosts/windows-vm;
      isWsl = true;
      platformModules = [ nixos-wsl.nixosModules.default ];
    };
  };

  # ...
}

NixOSとWSLで重複していたnixosSystemやHome Managerの組み込み処理は、mkNixosConfigurationへ集約しました。各出力にはホストモジュール、WSLかどうか、追加するプラットフォームモジュールだけを渡しています。

flake.nixが担っているのは、主に次の役割です。

  1. nixpkgsやHome Managerなどの入力を定義する
  2. CPUアーキテクチャごとのパッケージセットを用意する
  3. 各ホストで評価するモジュールを選択する
  4. モジュールが必要とする値を渡す
  5. 複数の出力で重複する構成生成処理を関数化する
  6. nixosConfigurationsなどの出力を公開する

このように、flake.nixは各環境で使用するモジュールとパッケージセットを選び、それらをNixOS、macOS、Nix-on-Droidの構成として公開します。分割した設定が、どの端末の構成へ組み込まれるのかを示す役割を担っています。

3.2. specialArgsextraSpecialArgsで値を渡す

モジュールを分割すると、共通値や追加のパッケージセットを各モジュールへ渡したくなります。

NixOSモジュールにはspecialArgs、Home Managerモジュールにはhome-manager.extraSpecialArgsを使用できます。

let
  # Git管理できる公開設定を読み込み、必要な値だけ取り出す
  localConfig = import ./config;
  inherit (localConfig) codexModels syncthing username;

  # Home Managerへ渡す共通値を1つの関数へ集約する
  mkHomeManagerExtraArgs =
    {
      unstablePackages,
      isNixOnDroid ? false,
      isWsl ? false,
    }:
    {
      # 各環境向けのunstableパッケージセットを同じ名前で参照できるようにする
      unstable = unstablePackages;
      guiPkgs = unstablePackages;
      inherit codexModels herdr isNixOnDroid isWsl syncthing;
    };

  # NixOSシステムモジュールへ渡す値はHome Manager用と分ける
  nixosSpecialArgs = {
    inherit username;
    unstable = nixosUnstable;
    guiPkgs = nixosUnstable;
  };
in
{
  nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
    system = systems.linux;

    # NixOSモジュールの引数として共通値を渡す
    specialArgs = nixosSpecialArgs;

    modules = [
      ./hosts/xps15/configuration.nix
      home-manager.nixosModules.home-manager
      {
        # Home Managerモジュールの引数は専用のoptionから渡す
        home-manager.extraSpecialArgs = mkHomeManagerExtraArgs {
          unstablePackages = nixosUnstable;
        };
      }
    ];
  };

  # ...
}

渡された値は、モジュールの引数として受け取れます。

# NixOSモジュール
{ pkgs, username, ... }:

{
  # flake.nixから受け取ったユーザーのログインshellを設定する
  users.users."${username}".shell = pkgs.zsh;

  # ...
}
# Home Managerモジュール
{ syncthing, unstable, ... }:

{
  # flake.nixから受け取ったunstableパッケージセットを利用する
  home.packages = [ unstable.terraform ];
  # 公開設定から受け取ったフォルダーIDをSyncthing設定へ埋め込む
  services.syncthing.settings.folders."${syncthing.obsidianFolderId}" = {
    label = "obsidian";
  };

  # ...
}

ここで注意したいのは、specialArgsを何でも入れるグローバル変数置き場にしないことです。

今回渡しているのは、複数のモジュールで必要になる次のような値です。

  • username: システム設定とHome Managerの接続に必要
  • unstable: 一部のツールだけ新しいパッケージセットから取得するために必要
  • codexModelsherdr: 全環境で使うツール設定に必要
  • isWslisNixOnDroid: 共通処理のうち、特定環境でだけ必要な差分を判断するために必要

Home Managerへ渡す共通値はmkHomeManagerExtraArgsへ集約し、isWslisNixOnDroidだけを環境ごとの差分として渡しています。特定のモジュールでしか使わない値は、そのモジュール内に置くか、専用のオプションとして定義した方が依存関係を把握しやすくなります。

4. 新しい設定の置き場所を決める

ここまでで、既存の設定をどう分け、どう組み合わせるかを見てきました。

この章では、自分の環境へ新しいツールや端末を追加する場面に、その考え方を当てはめます。例えば「全端末で同じGitの設定を使いたい」なら全環境共通のユーザー設定ですが、「特定のPCに接続したUSB機器を自動接続したい」なら、そのPCに依存する値を切り分ける必要があります。

4.1節では、こうした配置の判断を順に確認できる手順へまとめます。

4.2節では、配置先を決めた後の話として、複数環境で設定を共通化する際の注意点を確認します。共通化の範囲や依存関係の境界を、実際に起きやすかった問題から整理します。

4.1. 設定の配置先を判断する

新しい設定を追加するときは、次の順序で判断します。

設定を追加したい
    ↓
入力・出力・モジュールの接続か?
    ├── はい → flake.nix
    └── いいえ
         ↓
複数のモジュールで使う公開可能な値か?
    ├── はい → config
    └── いいえ
         ↓
複数のモジュールで使う純粋なNixの生成処理か?
    ├── はい → lib
    └── いいえ
         ↓
すべての環境で使うユーザー設定か?
    ├── はい → home/base
    └── いいえ
         ↓
同じOS系統で共有する設定か?
    ├── はい → modules/nixos または home/nixos
    └── いいえ
         ↓
macOS・WSL・Nix-on-Droid固有の仕組みか?
    ├── はい → modules/<environment> または home/<environment>
    └── いいえ
         ↓
特定端末やハードウェアに依存するか?
    ├── はい → hosts/<host>
    └── いいえ → 既存の分類を再確認する
                 秘密情報ならGit管理外の仕組みへ分離する

判断するときの観点を、表にまとめると次のようになります。

観点 確認すること
対象 どの環境に適用するか
管理対象 システム構成か、ユーザー環境か
API NixOS、nix-darwin、Nix-on-Droidのどのオプションか
ハードウェア 端末、GPU、USBポートなどに依存するか
パッケージ 対象プラットフォームで利用できるか
変更影響 別環境を評価したときにも影響するか

以上の手順では、設定の適用範囲、管理対象、利用するAPI、ハードウェアへの依存を順に確認し、共有設定、環境別設定、ホスト固有設定のどこへ置くかを決めます。この分類は、設定ファイルの見た目を整えるためだけのものではなく、変更の影響範囲をコード上で表現するためのものです。

配置先を決めたら、次に考えるのは、その設定をどこまで共通化してよいかです。

4.2. 共通化で起きやすかった問題

共通化する範囲が広すぎたり、異なる責務を同じ場所で扱ったりすると、変更の影響が意図しない環境まで広がります。

今後、共通化による問題が起きたときに、原因を切り分けて設定の分離や依存関係の見直しへすぐ移れるよう、共通モジュールへの設定の集約、システム設定とHome Manager設定の混同、同じパッケージセットの一律適用、公開設定と秘密情報の混在という4つの例から、注意すべき境界を確認しておきましょう。

すべてをhome/baseへ置く

共通モジュールへ設定を集めすぎると、条件分岐が増えていきます。

# 分岐が増え続ける例
home.packages =
  # OSや実行環境が増えるたびに、共通モジュールへ条件が追加される
  lib.optionals pkgs.stdenv.hostPlatform.isDarwin darwinPackages
  ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux linuxPackages
  ++ lib.optionals isWsl wslPackages
  ++ lib.optionals isNixOnDroid droidPackages;

小さなパッケージ差分であれば問題ありませんが、設定全体が環境ごとに異なる場合、すでに共通設定ではありません。

判断基準として、条件分岐の中にサービス設定、起動スクリプト、複数のパッケージがまとまって入り始めたら、環境別モジュールへ分離するよう検討しましょう。

システム設定とHome Manager設定を混同する

例えば、次の設定はシステム側の責務です。

  • OSユーザーの作成
  • Dockerデーモン
  • OpenSSHサーバー
  • システムフォント
  • udevルール

一方、次の設定はHome Manager側の責務です。

  • zshの設定
  • Gitの設定
  • ユーザーが使うCLI
  • ~/.config以下の設定ファイル
  • ユーザー単位のサービス

動作する場所が同じNixOSであっても、管理する状態が異なります。配置先を分けることで、設定に必要な権限と適用タイミングが明確になります。

すべての環境で同じNixpkgsを使う

同じflake.lockを使うことと、すべての環境へ同じパッケージセットを強制することは別です。

今回の構成では、通常のNixOS・macOSで使うnixpkgs、新しいパッケージを一部取得するnixpkgs-unstable、Nix-on-Droidで使うnixpkgs-droidに入力を分けています。

inputs = {
  # ...

  # 通常のNixOS・macOSで共有する安定版
  nixpkgs.url = "github:nixos/nixpkgs/<release>";
  # 一部の新しいパッケージを取得する更新版
  nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
  # PRootとの互換性を確認したNix-on-Droid専用revision
  nixpkgs-droid.url = "github:NixOS/nixpkgs/<known-working-revision>";

  # ...
};

Nix-on-DroidはAndroid上でprootなどを利用するため、通常のNixOS環境と同じ更新を行うと互換性問題の影響を受ける場合があります。そのため、現在の構成ではNix-on-Droid用の入力だけ既知の動作するリビジョンへ固定しています。

全体を一律に更新しやすくするよりも、互換性の境界に合わせて入力を分ける方が安全なケースもあります。

公開設定と秘密情報を混ぜる

共通値をconfig/default.nixへまとめていますが、ここへ秘密情報は置きません。

{
  # ...

  # 複数モジュールから参照し、このリポジトリでは公開可能と判断した識別子
  syncthing = {
    obsidianFolderId = "<folder-id>";
    pixel7proDeviceId = "<device-id>";
  };

  # システム設定とHome Manager設定で共有するユーザー名
  username = "your-username";

  # ...
}

ユーザー名やSyncthingのフォルダーID、デバイスIDのように、複数のモジュールから参照し、このリポジトリでは公開可能と判断した値は、Git管理されたNix式から読み込みます。これらのIDは認証用の秘密鍵ではありませんが、環境を識別する情報ではあるため、公開するかはリポジトリの運用方針に応じて判断します。環境変数へ依存せずFlakeの入力として扱えるため、macOS、NixOS、WSLの構成を純粋評価しやすくなります。

一方、次の値は同じ方法で管理しません。

  • APIキー
  • アクセストークン
  • パスワード
  • SSH秘密鍵
  • 公開したくないGitのユーザー情報

特に秘密をNix式へ直接書くと、評価やビルドを通してNixストアへ残る可能性があります。

「端末によって異なる値」と「秘密情報」は同じではありません。端末差分はhostsや公開設定で管理し、秘密情報はsops/age、パスワードマネージャー、Git管理外のローカル設定など、別の境界で管理する必要があります。

Nix-on-Droidでは、公式READMEに記載されているprootの制約により、Flake構成が内部的に--impureで評価されます。これはNix-on-Droidの実装上必要な動作であり、設定値を環境変数から読み込む設計を採用しているわけではありません。そのため、前述した「環境変数へ依存せず、Flakeの入力として扱う」という方針とは矛盾しません。

ここまで、Nixで管理する対象を整理し、設定を共有範囲と責務で分け、flake.nixで端末ごとに組み合わせる方法を見てきました。さらに、新しい設定の配置先と、共通化する際に守る境界も確認しました。これで、複数環境を管理する構成を設計し、設定を追加するまでの判断材料がそろいました。

ただし、配置先と共有範囲を決めただけでは、その設定が対象とするすべての環境で成立するかまでは分かりません。

最後の章では、変更を各環境で検証する方法を確認し、最終的なディレクトリ構成を振り返ります。

5. 変更が各環境で使えるか確認する

共有する設定を変更すると、その設定を使うすべての環境へ影響する可能性があります。

手元のMacで問題なく使えても、NixOS側では利用できないソフトウェアが含まれているかもしれません。そこで、変更を反映する前に、対象となる環境ごとに評価やビルドを行い、意図した範囲で利用できることを確認します。

5.1節では、設定を読み取って必要な値を求める「評価」、必要なソフトウェアや設定ファイルなどを用意する「ビルド」、それらを端末へ適用する「反映」の段階を区別して説明します。評価が通ることは確認の一段階であり、実際にソフトウェアを起動して使えるかは、対象の端末でも確かめる必要があります。

5.2節では、最終的なディレクトリ構成を見ながら、ここまで説明した共有設定・環境別設定・ホスト固有設定がどこへ配置されたかを振り返ります。変更の確認方法とファイル配置を結び付けて、次の設定追加でも使えるようにします。

5.1. 各環境を個別に検証する

複数環境を同じFlakeで管理する場合、変更した端末でswitchできたことだけでは十分ではありません。

例えば、macOS向けの変更にNixOS非対応のパッケージが混ざっても、macOSの評価だけでは別環境への影響を確認できません。

まず、フォーマットと静的解析を実行します。

# 全Nixファイルがnixfmtの整形結果と一致するか確認する
nix fmt -- --check $(git ls-files '*.nix')

# 未使用・冗長なNix式などを静的解析する
statix check .

nix fmtは、現在のFlakeに定義したformatter.<system>を呼び出します。このリポジトリではaarch64-darwinx86_64-linuxaarch64-linuxのformatterを、flake.lockで固定したパッケージセットから生成しています。

次に、nix flake checkの検査対象になる出力を、異なるsystemの出力も含めて確認します。

# 標準的なFlake出力を全systemについて検査し、checksのビルドは省略する
nix flake check --all-systems --no-build --no-warn-dirty

この構成ではnixosConfigurationsdarwinConfigurationsが検査されます。一方、nixOnDroidConfigurationsのような独自形式の出力は検査対象外になるため、このコマンドだけで管理対象のすべてを確認できるわけではありません。

そこで、対象ごとのシステム構成をビルドするための値を個別に評価します。Flake参照はシェルに記号を解釈されないよう、引用符で囲みます。

# 🍎 macOS
nix eval --raw \
  '.#darwinConfigurations.novumdnoMac-mini.config.system.build.toplevel.drvPath'

# ❄️ NixOS
nix eval --raw \
  '.#nixosConfigurations.nixos.config.system.build.toplevel.drvPath'

# 🟦 WSL
nix eval --raw \
  '.#nixosConfigurations.windows-vm.config.system.build.toplevel.drvPath'

# 🟢 Nix-on-Droidのシェル設定だけを確認する簡易評価
nix eval --raw \
  '.#nixOnDroidConfigurations.pixel7pro.config.user.shell'

ここで使っているnovumdnoMac-mininixoswindows-vmpixel7proは、このリポジトリで定義している出力名です。別の構成で試す場合は、自分のflake.nixで定義した出力名へ置き換えます。

NixOS、WSL、macOSでdrvPathを取得すると、システム構成を表す導出を評価できますが、構成そのものはビルドしません。一方、Nix-on-Droidの例で取得しているのはconfig.user.shellだけです。Nixは必要な属性を遅延評価するため、このコマンドはシェル設定の簡易確認であり、activationPackage全体の評価を保証するものではありません。

各環境の評価対象、反映を伴わないビルド、対象端末への反映方法は、次の通りです。

🍎 macOS

  • 評価: darwinConfigurations.novumdnoMac-mini.config.system.build.toplevel.drvPath
  • ビルド: nix build '.#darwinConfigurations.novumdnoMac-mini.config.system.build.toplevel'
  • 反映: sudo darwin-rebuild switch --flake '.#novumdnoMac-mini'

❄️ NixOS

  • 評価: nixosConfigurations.nixos.config.system.build.toplevel.drvPath
  • ビルド: nix build '.#nixosConfigurations.nixos.config.system.build.toplevel'
  • 反映: sudo nixos-rebuild switch --flake '.#nixos'

🟦 WSL

  • 評価: nixosConfigurations.windows-vm.config.system.build.toplevel.drvPath
  • ビルド: nix build '.#nixosConfigurations.windows-vm.config.system.build.toplevel'
  • 反映: sudo nixos-rebuild switch --flake '.#windows-vm'

🟢 Nix-on-Droid

  • 評価: nixOnDroidConfigurations.pixel7pro.config.user.shell(簡易評価)
  • ビルド: switch時に端末上で実行
  • 反映: nix-on-droid switch --flake '.#pixel7pro'

異なるCPU・OS向けの構成は、対応するビルダーがなければ手元でビルドできません。また、Nix-on-Droidにはprootの固定Storeパスに由来する制約があり、nix-on-droidがFlake構成を--impureで評価します。そのため、PC上の簡易評価に加え、最終的なビルドとswitchをAndroid端末上で確認します。

Nixファイルを変更
    ↓
フォーマット・静的解析
    ↓
標準的なFlake出力を全systemについて検査
    ↓
管理対象の出力を個別に評価
    ↓
対応するビルダーで対象の出力をビルド
    ↓
対象端末でswitch

この順序にすることで、構文エラーや他のプラットフォームへの影響を、システムへ反映する前に発見しやすくなります。

5.2. 現在のディレクトリ構成

最終的な構成は次のようになりました。

dotfiles
├── flake.nix
├── flake.lock
├── boot-strap.sh
├── config
│   └── default.nix
├── home
│   ├── base
│   │   ├── default.nix
│   │   └── programs
│   ├── darwin
│   │   ├── default.nix
│   │   └── programs
│   └── nixos
│       └── default.nix
├── lib
│   └── android-studio.nix
├── modules
│   ├── darwin
│   │   └── default.nix
│   ├── nixos
│   │   └── common.nix
│   ├── wsl
│   │   └── default.nix
│   └── nix-on-droid
│       └── default.nix
└── hosts
    ├── Mac-mini
    ├── xps15
    ├── windows-vm
    └── pixel7pro

この構成に含まれる主要なファイルと、設定を分割するディレクトリの責務を分けてまとめます。

ルート直下のファイルは、Flake全体の入口や再現性、導入手順を担います。

ファイル 責務
flake.nix 入力、出力、パッケージセット、モジュール間の接続を定義する
flake.lock 入力のリビジョンを固定し、同じ依存関係を再現できるようにする
boot-strap.sh Nixによる反映前に必要な基本ツールを導入する

設定の実装は、共有範囲と管理対象に応じて次のディレクトリへ分割します。

ディレクトリ 責務
config 複数のモジュールで共有する、秘密を含まない公開可能な値を置く
home/base 全環境共通のHome Manager設定を置く
home/darwin macOS固有のHome Manager設定を置く
home/nixos NixOSとWSLで共有するHome Manager設定を置く
lib 複数のモジュールから使う純粋なNix関数を置く
modules/nixos 実機とWSL上のNixOSで共有するシステム設定を置く
modules/darwin macOS固有のシステム設定を置く
modules/wsl WSL固有のシステム設定を置く
modules/nix-on-droid Nix-on-Droid環境固有の設定を置く
hosts 端末やハードウェアに固有の設定を置く

default.nixcommon.nixなどの個別ファイルは、それぞれが属するディレクトリの設定を実装したり、まとめて読み込む入口になったりします。そのため、表ではファイルを一つずつ列挙せず、共有範囲を表すディレクトリ単位で責務を示しています。

まとめ

本記事では、macOS、NixOS、WSL、Nix-on-Droidを1つのFlakeで管理する構成を題材に、設定の分類から検証までを順に整理しました。これまで説明した内容の要点は、次の通りです。

  • NixOS、nix-darwin、Nix-on-Droidの各モジュールシステムはそれぞれの環境を、Home Managerはユーザー環境を管理し、Nix Flakeが依存関係と各端末の構成をまとめる
  • 設定は「どの環境で使うか」という適用範囲と、「何を管理するか」という管理対象の2軸で分類する
  • 適用範囲は「全環境共通・OS系統共通・プラットフォーム固有・ホスト固有」の4層に分ける
  • 管理対象は「システム設定」と「Home Managerのユーザー設定」に分け、共有するシステム設定はmodules、ユーザー設定はhome、ホスト固有の差分はhostsへ配置する
  • 複数のモジュールで共有する公開可能な値はconfig、管理対象をまたいで再利用する純粋なNix関数はlibへ分ける
  • flake.nixでは、各環境に必要な入力、パッケージセット、モジュールを選び、必要な値を渡して出力として公開する
  • 共通化するときは、条件分岐の増加、システム設定とユーザー設定の混同、プラットフォームごとの互換性、公開設定と秘密情報の境界に注意する
  • 変更後は、フォーマット・静的解析、標準的なFlake出力の検査、管理対象の個別評価、対応するビルダーでのビルド、対象端末での反映という順に確認する

さいごに

Nixで複数環境を管理する目的は、すべての端末を完全に同じ状態にすることではありません。共通にできる設定を再利用しながら、OS、実行環境、端末ごとの違いを、意図した差分として構成に残すことにあります。

そのためには、新しい設定を追加するたびに「どの環境で使うのか」「システムとユーザー環境のどちらを管理するのか」「端末や依存関係に固有の条件があるか」を確認することが大切です。この判断がファイル配置に表れていれば、変更の影響範囲を構成から読み取りやすくなります。

新しいツールや端末を追加するときは、第4章の手順で配置先と共通化の範囲を決め、第5章の手順で各環境への影響を確認します。

この流れを繰り返すことで、管理する環境が増えても、共通部分を再利用しながら、各環境に必要な差分を適切に保てる構成を維持できます。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?