自宅サーバーに複数のサービスを載せたい: microvm.nix編
そもそもmicrovm.nixの使い方がよくわかんないので、ここをなんとかしないといけません。
公式ドキュメント
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経由でインターネットに繋ぐというかなり特殊な構成になるので事情が変わります。
というわけで次回、ルーター編になります。

