blog.anqou.net
rss
author
tags

nixos-anywhere で NixOS を VPS にインストールする

NixOS をインストールする際、通常は NixOS の公式サイトで配布されているライブイメージを USB メモリに焼いてブートし、各種コマンドなどを手動で実行して NixOS をインストールします。ただ、場合によってはこの方法が使えなかったり、使いにくいことがあります。

例えば、最近自分はわくわく鮟鱇ランドのサーバーを KAGOYA Cloud VPS に載せ替えたのですが、KAGOYA は新規に VM を作成する場合はカスタムの ISO イメージを使えるものの、既存の VM を利用して OS を再インストールしたい場合にはそれが使えません[1]。そのため、そもそも NixOS のインストール作業に入ることができません。あるいは物理的なマシンでも、手が届きにくいところに LAN だけ繋がったマシンが置いてあったり、セットアップしたいマシンが大量にあるような場合には、手動でちまちまインストールするのは面倒です。

このようなケースで NixOS をインストールする場合は nixos-anywhere が便利です。 nixos-anywhere は遠隔でつながるマシンに対して NixOS をインストールするためのツールで、あらかじめ configuration.nix などのファイルを用意しておくことで、その設定で NixOS を SSH ごしにインストールできます。内部的には kexec を活用してカーネルのスイッチを行います。そのため、NixOS がインストールされる側のマシンは SSH ごしに(root で繋がって kexec が使える)Linux が露出されていればよく、適当な Linux distro のライブ USB がブートされただけの状態でも問題ありません[2]

nixos-anywhere 公式の Quickstart Guide が丁寧に書かれているので、これを見ると使い方はおおよそわかります。以下は KAGOYA Cloud VPS で nixos-anywhere を使った際の記録です。予め KAGOYA の Web UI から「初期化」ボタンを押し、ターゲットの VM には AlmaLinux 9 をインストールしてあります。

まず Nix Flake の依存に disko を追加します。disko は nixos-anywhere が前提にするツールで、ディスクのセットアップ(パーティションやLVM の設定など)を行います:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05-small";

    disko = {
      url = "github:nix-community/disko";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = {self, nixpkgs, disko}: {
    nixosConfigurations = {
      kagoya = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [./configuration.nix disko.nixosModules.disko];
      };
    };
  };
}

続いて configuration.nix を用意します。基本的に nixos-anywhere の example コードをそのまま使えば良いですが、状況に合わせて修正も必要です。KAGOYA の場合、IP アドレスは固定なので、それを前提にしたネットワーク設定を盛り込んでおく必要があります。この辺りは以前書いた記事を参考にしてください。

{lib, pkgs, ...} @ args:
{
  imports = [
    ./disk-config.nix # 後で作る
    ./hardware-configuration.nix # nixos-anywhere が自動生成する
  ];

  # KAGOYA は BIOS のみの対応なので nixos-anywhere を使う場合
  # boot.loader.grub は空で良い。
  #boot.loader.grub = {
  #  # no need to set devices, disko will add all devices that have a EF02 partition to the list already
  #  # devices = [ ];
  #  efiSupport = true;
  #  efiInstallAsRemovable = true;
  #};
  services.openssh.enable = true;

  # 固定 IP アドレス用の設定を書く
  systemd.network = {
    enable = true;
    networks."10-wan" = {
      # インターネットに出ていける NIC
      matchConfig.Name = "eno1";
      # 割り当てられた IP アドレスとサブネットマスク
      address = ["198.51.100.100/24"];
      routes = [
        {
          # デフォルトゲートウェイ
          Gateway = "198.51.100.1";
        }
      ];
      linkConfig.RequiredForOnline = "routable";
    };
  };
  networking = {
    hostname = "kagoya";
    useDHCP = false; # DHCP を無効化
    defaultGateway = "198.51.100.1"; # デフォルトゲートウェイ
    # DNS サーバ
    nameservers = ["198.51.100.200" "198.51.100.201"];
  };

  environment.systemPackages = map lib.lowPrio [
    pkgs.curl
    pkgs.gitMinimal
  ];

  users.users.root.openssh.authorizedKeys.keys =
  [
    # 手元のマシンの公開鍵を設定しておく。
    "ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX your-system"
  ] ++ (args.extraPublicKeys or []); # this is used for unit-testing this module and can be removed if not needed

  system.stateVersion = "26.05";
}

さらに imports で読み込むための disk-config.nix を作ります。これは disko の設定ファイルで、ディスクをどのようにセットアップするかを指定します。disko の example ディレクトリに様々なケースでどのように書くべきかという例があるので、基本的にはこれを見ればなんとなく雰囲気がわかります。今回は gpt-bios-compat.nix を参考に以下のようにしました:

{
  disko.devices = {
    disk = {
      main = {
        device = "/dev/vda";
        type = "disk";
        content = {
          type = "gpt";
          partitions = {
            boot = {
              size = "1M";
              type = "EF02"; # for grub MBR
              attributes = [ 0 ]; # partition attribute
            };
            root = {
              size = "100%";
              content = {
                type = "filesystem";
                format = "ext4";
                mountpoint = "/";
              };
            };
          };
        };
      };
    };
  };
}

これで用意すべきファイルは全てなので nix flake lock して flake.lock を更新した後、以下のように nixos-anywhere を実行します:

nix run github:nix-community/nixos-anywhere -- \
    --generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
    --flake .#kagoya \
    --target-host root@(VM の IP アドレス)

セットアップが正しければ、これで接続先のホストに NixOS がインストールされます。ダメだった場合は KAGOYA の Web UI から AlmaLinux 9 を再インストールして再挑戦しましょう(1 敗)[3]

注釈

  1. 明示的には書かれていない気がしますが、Web UI にボタンが表示されません。

  2. 特に NixOS のライブ USB がブートされている状況だと NixOS の中身をわざわざダウンロードしてくる必要がないので効率的に動作するようです。

  3. nixos-anywhere には手元で VM を立ち上げて動作確認をするための手順も記載されているので、慎重な方はそちらを試してから実際に実行するのが良いでしょう。