• 作成:

NixOSでWindowsとデュアルブートするならgrubを使うべき

背景

最近NixOSに移行しました。

まずラップトップPCに修理から帰ってきた後にNixOSをインストールして、その後デスクトップPCのネイティブGNU/Linux環境もNixOSにしました。

ラップトップPCのデュアルブート

インストールする前に環境を記述したときはgrubを使うように設定していました。これは移行前のGentooではgrubを使っていたためです。しかしインストール時にエラーが起きたので、 Claudeに聞いてみたらsystemd-bootを使うことが推奨されました。

systemd-bootで問題なく動いているのでそのままにしています。

ラップトップPCのストレージはSSDが1枚だけなので、そのまま自動検出でWindowsとのデュアルブートも出来ます。

デスクトップPCのストレージ環境

私のデスクトップPCには現在3枚のM.2 NVMe SSDが搭載されています。

用途は以下の通りです。

  1. ネイティブGNU/Linux環境
  2. ゲーム用のWindows 11
  3. 仕事用のWindows 11(WSL2ばかり使っているので実質的にGNU/Linux環境)

3枚目のWindows 11環境のWSL2は、他のネイティブGNU/Linux環境以前にNixOSをインストールしたので、最初にNixOSに移行したマシンと言えます。

完全にディスクを分けているのは、たまにWindowsが勝手にEFIパーティション含めてフォーマットしてしまうからです。完全に分けていればあまり干渉することはないですし、性能向上や故障の修理などの目的でSSDを一部だけ交換しても他のOSに影響は出にくいです。仮にどれかが故障しても、 UEFIのブートメニューから直接ディスクを指定すれば独立して起動します。

systemd-bootは同じディスクにあるOSしか自動検出しない

ラップトップPCではsystemd-bootがEFIパーティションの他のOSを自動検出してくれました。これは同じEFIパーティションを使っているためです。

しかしデスクトップPCでは自動検出してくれませんでした。ディスクごとにEFIパーティションを分割しているためです。

systemd-bootに手動でエントリを追加すればブートは可能

以下のように手動で記述すればWindowsを起動メニューに含めることができます。

edk2-uefi-shell.enable = true;
# 仕事用Windowsを最上位に表示し、上キーで移動できるようにする。
windows = {
  "work" = {
    title = "Windows 11 Work";
    efiDeviceHandle = "HD0b";
    sortKey = "a_windows_work";
  };
  "game" = {
    title = "Windows 11 Game";
    efiDeviceHandle = "HD1b";
    sortKey = "b_windows_game";
  };

まずedk2-uefi-shell.enable = true;でデバッグシェルを有効にして、起動してmap -cでデバイス名を確認します。写真に撮っておくと良いでしょう。

そしてNixOSを起動して、 blkidコマンドを実行して、写真のEFIパーティションのUUIDを確認すれば、 efiDeviceHandleに指定するデバイス名が分かります。

最上位にWindowsを置いているのは、 NixOSは世代ごとにブートのメニューをたくさん作るので、デフォルトはNixOSの最新版にして、下側に各世代のNixOSをずらずら並べてもらって、上キーで簡単にWindowsを選べるようにしたいからです。

systemd-boot経由だとWindowsを起動するたびにBitLockerの回復キーの入力が必要

しかしこの方法でWindowsを起動すると、毎回BitLockerの回復キーを入力する必要が発生してしまいます。

普通はBitLockerはUEFIとかハードウェアの重大な変更がない限り回復キーをスキップしてくれるのですが。

少し調べてみたところsystemd-bootだと難しいらしいです。

boot.loader.systemd-boot.rebootForBitlocker というオプションがあるのでこれを使えば良いのかと思ったのですが、これもやはりEFIパーティションが同じでないとダメなようです。

rEFIndはNixOSの世代ブートに対応していない

The rEFInd Boot Manager は豪華で様々な機能を持つブートローダーですが、 NixOSの世代ブートには対応していないので、そこからsystemd-bootなどのブートローダを連鎖して起動する必要があります。 rEFIndが便利なことがあるのかもしれませんが、今回は流石にめんどくさいので使いませんでした。

grubを使えば解決可能

私はGentooを使っていたときはgrubを使っていたので、 grubを使えばBitLockerの回復キーの入力をスキップできることを知っていました。

別にgrubを嫌っているわけでもないので、 grubで起動できるならそれで良いと考えてgrubを使うことにしました。

grubを使えば素直に以下のようにWindowsの起動設定を記述できます。

grub = {
  enable = true;
  device = "nodev";
  efiSupport = true;
  extraEntries = ''
    menuentry "Windows Game" {
      insmod part_gpt
      insmod fat
      insmod chain
      set root='hd0,gpt1'
      chainloader /efi/Microsoft/Boot/bootmgfw.efi
    }
    menuentry "Windows Work" {
      insmod part_gpt
      insmod fat
      insmod chain
      set root='hd1,gpt1'
      chainloader /efi/Microsoft/Boot/bootmgfw.efi
    }
  '';
};

os-proberを使わずに手動で記述しているのは、私は2つのWindowsを使っているので、どちらのブートエントリなのか明確に表示された方が嬉しいからです。 GNU/LinuxとWindowsのそれぞれ一つだけしかない場合はos-proberを使ったほうが楽ができると思います。

と言うわけでWindowsとデュアルブートしている人はgrubを使ったほうが良いでしょう。別にgrubの質が低いわけではないですし。