Ideal Reality

興味の赴くままに
  1. トップ
  2. 投稿
  3. 自宅サーバーに複数のサービスを載せたい: microvm.nix編
add_22026年1月10日 19時46分

自宅サーバーに複数のサービスを載せたい: microvm.nix編

そもそもmicrovm.nixの使い方がよくわかんないので、ここをなんとかしないといけません。

スポンサーリンク

公式ドキュメント

GitHub - microvm-nix/microvm.nix: NixOS MicroVMs

NixOS MicroVMs. Contribute to microvm-nix/microvm.nix development by creating an account on GitHub.

GitHubのREADMEとかドキュメントがありますが、どこから手をつけていいか分からないですね。
(Nix関連のエコシステムって大体そうで、一番最初のハードルがとても高いと思ってます。)

microvm.nixの使い方

最初から最大の躓きポイントです。とってもややこしいんですけど、microvm.nixの使い方には2通りの方法があります。(この記述が正しいのか分からないけど、自分はそう認識したほうが理解できた)

1つは、flakeにVMの構成を記述して、直接手元のマシンでVMを起動する方法。
flake.nixのあるgitレポジトリをcloneしてそのままnix runでVMが起動できる感じで、Docker Composeみたいな使い方ができます。
最初に記述されているExampleはこっちの方法です。この場合、HostOSの環境はflakeの管理外なので、nixosConfigurationsには直接VMの構成を入れることになります。

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  inputs.microvm.url = "github:microvm-nix/microvm.nix";
  inputs.microvm.inputs.nixpkgs.follows = "nixpkgs";

  outputs = { self, nixpkgs, microvm }: {
    # nixosConfigurationsにVMインスタンスの構成を記述
    nixosConfigurations.my-microvm = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        # VM用のmoduleを読み込み
        microvm.nixosModules.microvm
        {
          # VM自体の設定
          microvm = {
            mem = 4096;
            vcpu = 2;
            hypervisor = "qemu";
          };

          # あとはVM上のNixOSの設定(いつも通り)
          networking.hostName = "my-microvm";
        }
      ];
    };
  };
}

もう1つは、NixOSのconfigurationにVMの構成を記述して、systemdのサービスとしてVMを起動する方法です。今回僕がやりたいのはこっちの方法。
この場合、HostOSの環境の一部としてVMが記述されるので、nixosConfigurationsにはHostOSの構成を入れて、その中で起動したいVMの構成を記述することになります。

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  inputs.microvm.url = "github:microvm-nix/microvm.nix";
  inputs.microvm.inputs.nixpkgs.follows = "nixpkgs";

  outputs = { self, nixpkgs, microvm }: {
    # nixosConfigurationsで記述するのはHostOSの構成
    nixosConfigurations.server1 = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        # host用のmoduleを読み込み
        microvm.nixosModules.host
        {
          # HostOS上のNixOSの設定(いつも通り)
          networking.hostName = "server1";

          # 起動するVMの設定
          microvm.vms.my-microvm.config = {
            # VM自体の設定
            microvm = {
              mem = 4096;
              vcpu = 2;
              hypervisor = "qemu";
            };

            # あとはVM上のNixOSの設定(いつも通り)
            networking.hostName = "my-microvm";
          };
        }
      ];
    };
  };
}

もちろん、実運用上はファイルを分けたりしてもっと管理しやすくするわけですが、全体的な構成としてはこんな感じになります。

スポンサーリンク

最小限の環境

ここで最大の躓きポイントです。microvmはコンソールの出力をjournaldに出すので、起動中のログとかはjournalctlで見れるのですが、シェルに入ってコマンドを実行することができません。
なので、VMのシェルを操作したい場合は、HostOSとTapで繋いでsshする必要があります。これ、Tap接続でトラブルが発生してもVM側の設定を直接いじったりできないためかなり大変です。
そこでまずはVMを起動してsshで操作できる最低限の環境構築を目指しましょう。
(以下で記述するconfigurationは実際に動作確認したものじゃなくてあくまで参考に書いたものである点ご了承ください。)

{
  description = "microvm.nix Test Environment";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
    microvm = {
      url = "github:microvm-nix/microvm.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs@{ self, nixpkgs, ... }: {
    nixosConfigurations."server" = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      specialArgs = { inherit inputs; };
      modules = [
        ./hardware-configuration.nix
        ./server.nix
      ];
    };
  }
}
{ config, pkgs, lib, inputs, ... }:
let
  user = "server";
  password = "HyperStrongPassword";
in {
  system.stateVersion = "25.11";
  imports = [
    inputs.microvm.nixosModules.host
  ];

  # Network
  systemd.network.enable = true;
  networking = {
    hostName = "server";
    useNetworkd = true;
    useDHCP = true;
  };

  # User
  users = {
    mutableUsers = lib.mkDefault false;
    users."${user}" = {
      isNormalUser = true;
      password = password;
      extraGroups = [ "wheel" ];
    };
  };

  # SSH Server
  services.openssh.enable = true;

  # MicroVM
  microvm.vms."testvm".config = import ./testvm.nix;
  systemd.network.networks."10-testvm" = {
    name = "testvm";
    address = [ "10.0.0.1/24" ];
  };
}
{ config, pkgs, lib, inputs, ... }:
let
  hostName = "testvm";
  user = "testvm";
  password = "SuperStrongPassword";
  macAddress = "52:54:00:a3:7b:2e";
  ipAddress = "10.0.0.2/24";
in {
  system.stateVersion = "25.11";

  # MARK: MicroVM
  microvm = {
    mem = 4096;
    vcpu = 4;
    hypervisor = "qemu";
    interfaces = [
      { type = "tap"; id = hostName; mac = macAddress; }
    ];
    shares = [
      { source = "/nix/store"; mountPoint = "/nix/.ro-store"; tag = "ro-store"; proto = "virtiofs"; }
    ];
  };

  # MARK: User
  users = {
    mutableUsers = lib.mkDefault false;
    users."${user}" = {
      isNormalUser = true;
      password = password;
      extraGroups = [ "wheel" ];
    };
  };

  # MARK: SSH Server
  services.openssh.enable = true;

  # MARK: Networking
  systemd.network.enable = true;
  networking = {
    hostName = hostName;
    useNetworkd = true;
    firewall.enable = false;
  };
  systemd.network.networks."40-tap" = {
    matchConfig.MACAddress = macAddress;
    address = [ ipAddress ];
  };
}

ネットワークの設定はsystemd-networkdが推奨です。この段階ではDHCPが使えず、かつNICの名前も不明なためMACアドレスを指定してIPアドレスを設定する必要があります。
ホスト側はtapデバイスの作成はmicrovmがやってくれるのでnetdevは不要、IPアドレスの設定だけやっておきましょう。あと以下の点は各環境に合わせて変更してください

  • passwordは mkpasswd -m yescrypt を使って生成した強力なhashedPasswordに変える
  • MACアドレスは 52:54:00 から始まるランダムなものにする

これでssh接続ができればOKです。
注意点として、なぜか microvm.mem を2048に設定するとVMが起動しなくなります。

以降の作業

これで最低限のVM環境が起動できたので、あとは好き勝手にいじるだけです。
普通はここからHostOS側でTAPに対してNAT変換するかbridgeを組んでVMをインターネットに繋ぐわけですが、まあそのあたりのネットワークの設定はqemu-kvmと変わらないので別にここで説明するまでもないでしょう。
僕は今回VMにルーターを置いて、HostOSがVM経由でインターネットに繋ぐというかなり特殊な構成になるので事情が変わります。

というわけで次回、ルーター編になります。

共有

この記事が役に立ったならば、シェアしていただけると嬉しいです。

スポンサーリンク
関連する投稿
自宅サーバーに複数のサービスを載せたい: 構想編

自分はルーターとしてMinisforum MS-01を使ってます。元々自宅サーバー用に用意した機材で、NICが沢山あって都合がよかったので採用したのですが、i5-12600H + 32GB RAMのマ...

2026年1月10日
プロフィール
Hiroki Kawakami

サイトを作り替えています

スポンサーリンク