···11+# MicroVM in Den examples
22+33+There are two ways to run VMs:
44+55+## NixOS configuration MicroVM runner as package.
66+77+- Den example: [runnable-example.nix](./modules/runnable-example.nix)
88+- Den support: [microvm-runners.nix](./modules/microvm-runners.nix)
99+1010+```console
1111+nix run .#runnable-microvm
1212+```
1313+1414+See https://microvm-nix.github.io/microvm.nix/packages.html
1515+1616+## MicroVM guests as part of a Host.
1717+1818+- Den example: [guests-example.nix](./modules/guests-example.nix)
1919+- Den support: [microvm-integration.nix](./modules/microvm-integration.nix)
2020+2121+```console
2222+nixos-rebuild build --flake .#server
2323+```
2424+2525+See https://microvm-nix.github.io/microvm.nix/host.html\
2626+https://microvm-nix.github.io/microvm.nix/declarative.html
···11+{ den, inputs, ... }:
22+{
33+ imports = [ inputs.den.flakeModule ];
44+55+ #
66+ # There are two ways to run VMS:
77+ #
88+ # - NixOS configuration MicroVM runner as package.
99+ # Den example: ./runnable-example.nix (by ./microvm-runners.nix)
1010+ # See https://microvm-nix.github.io/microvm.nix/packages.html
1111+ #
1212+ #
1313+ # - MicroVM guests as part of a Host.
1414+ # Den example: ./guests-example.nix (by ./microvm-integration.nix)
1515+ # See https://microvm-nix.github.io/microvm.nix/host.html
1616+ # https://microvm-nix.github.io/microvm.nix/declarative.html
1717+ #
1818+1919+ # automatically set hostname on all hosts.
2020+ den.ctx.host.includes = [ den._.hostname ];
2121+}
+38
templates/microvm/modules/guests-example.nix
···11+{ den, lib, ... }:
22+{
33+44+ den.hosts.x86_64-linux.server.microvm.guests = [
55+ den.hosts.x86_64-linux.guest-microvm
66+ ];
77+88+ den.hosts.x86_64-linux.guest-microvm = {
99+ intoAttr = [ ]; # dont produce Guest nixosConfiguration at flake output
1010+ };
1111+1212+ den.aspects.no-boot.nixos = {
1313+ boot.loader.grub.enable = false;
1414+ fileSystems."/".device = "/dev/null";
1515+ };
1616+1717+ den.aspects.server = {
1818+ # USER TODO: remove this on real bootable server
1919+ includes = [ den.aspects.no-boot ];
2020+2121+ # NOTE: no microvm class exist for Host, only for Guests
2222+ nixos.microvm.host.startupTimeout = 300;
2323+ };
2424+2525+ den.aspects.guest-microvm = {
2626+ # resolved with: `(den.ctx.host = { host = guest-microvm }).resolve { class = "nixos" }`
2727+ # resulting nixos-module is set at server: microvm.vms.<name>.config
2828+ nixos =
2929+ { pkgs, ... }:
3030+ {
3131+ environment.systemPackages = [ pkgs.cowsay ];
3232+ };
3333+3434+ # microvm class is for Guests!, forwarded into server: nixos.microvm.vms.<name>
3535+ microvm.autostart = true;
3636+ };
3737+3838+}
+116
templates/microvm/modules/microvm-integration.nix
···11+{
22+ den,
33+ lib,
44+ config,
55+ inputs,
66+ ...
77+}:
88+let
99+ # extends den.schema.host with MicroVM specific options
1010+ extendHostSchema =
1111+ { host, ... }:
1212+ {
1313+ options.microvm.module = lib.mkOption {
1414+ description = "MicroVM microvm.nix module";
1515+ type = lib.types.deferredModule;
1616+ default = inputs.microvm."${host.class}Modules".microvm;
1717+ };
1818+1919+ options.microvm.hostModule = lib.mkOption {
2020+ description = "MicroVM host.nix module";
2121+ type = lib.types.deferredModule;
2222+ default = inputs.microvm."${host.class}Modules".host;
2323+ };
2424+2525+ # Declarative Guest VMs built with Host.
2626+ options.microvm.guests = lib.mkOption {
2727+ type = lib.types.listOf lib.types.raw;
2828+ default = [ ];
2929+ description = ''
3030+ Guest MicroVMs.
3131+ Value is a list of Den hosts: [ den.hosts.x86_64-linux.foo-microvm ]
3232+3333+ When non empty, Host imports <microvm>/host.nix module
3434+ and starts our Den microvm-host context pipeline.
3535+3636+ See: https://microvm-nix.github.io/microvm.nix/host.html
3737+ https://microvm-nix.github.io/microvm.nix/declarative.html
3838+ '';
3939+ };
4040+4141+ options.microvm.sharedNixStore = lib.mkEnableOption "Auto share nix store from host";
4242+ config.microvm.sharedNixStore = lib.mkDefault true;
4343+ };
4444+4545+ # transition a NixOS host into a MicroVM host (only if it has guest microvms)
4646+ ctx.host.into.microvm-host = { host }: lib.optional (host.microvm.guests != [ ]) { inherit host; };
4747+4848+ # aspect configuring a MicroVM host. imports the microvm host.nix module.
4949+ ctx.microvm-host.provides.microvm-host =
5050+ { host }:
5151+ {
5252+ ${host.class}.imports = [ host.microvm.hostModule ];
5353+ };
5454+5555+ # transition from microvm host into each microvm guest
5656+ ctx.microvm-host.into.microvm-guest = { host }: map (vm: { inherit host vm; }) host.microvm.guests;
5757+5858+ # aspect configuring a guest vm at the host level (Declarative in MicroVM parlance)
5959+ # See: https://microvm-nix.github.io/microvm.nix/declarative.html
6060+ ctx.microvm-host.provides.microvm-guest =
6161+ { host, vm }:
6262+ {
6363+ includes =
6464+ let
6565+ sharedNixStore = lib.optionalAttrs host.microvm.sharedNixStore {
6666+ ${host.class}.microvm.vms.${vm.name}.config.microvm.shares = [
6767+ {
6868+ source = "/nix/store";
6969+ mountPoint = "/nix/.ro-store";
7070+ tag = "ro-store";
7171+ proto = "virtiofs";
7272+ }
7373+ ];
7474+ };
7575+7676+ # forwards guest nixos configuration into host: microvm.vms.<vm-name>.config
7777+ osFwd = den.provides.forward {
7878+ each = lib.singleton true;
7979+ fromClass = _: vm.class;
8080+ intoClass = _: host.class;
8181+ intoPath = _: [
8282+ "microvm"
8383+ "vms"
8484+ vm.name
8585+ "config"
8686+ ];
8787+ # calling host-pipeline ensure all Den features supported on guest
8888+ fromAspect = _: den.ctx.host { host = vm; };
8989+ };
9090+9191+ # forwards guest microvm class into host: microvm.vms.<vm-name>
9292+ microvmClass = den.provides.forward {
9393+ each = lib.singleton true;
9494+ fromClass = _: "microvm";
9595+ intoClass = _: host.class;
9696+ intoPath = _: [
9797+ "microvm"
9898+ "vms"
9999+ vm.name
100100+ ];
101101+ fromAspect = _: den.aspects.${vm.aspect};
102102+ };
103103+104104+ in
105105+ [
106106+ sharedNixStore
107107+ osFwd
108108+ microvmClass
109109+ ];
110110+ };
111111+112112+in
113113+{
114114+ den.ctx = ctx;
115115+ den.schema.host.imports = [ extendHostSchema ];
116116+}
+32
templates/microvm/modules/microvm-runners.nix
···11+# for each host exposes microvm declaredRunner (if exists) as package output of this flake.
22+# feel free to remove or adapt.
33+{
44+ den,
55+ lib,
66+ config,
77+ ...
88+}:
99+let
1010+ # omit if you are using flake-parts. create a packages output for us.
1111+ packagesModule.options.flake.packages = lib.mkOption { };
1212+1313+ microvmRunners = lib.pipe den.hosts [
1414+ lib.attrValues
1515+ (lib.concatMap lib.attrValues)
1616+ (map (
1717+ host:
1818+ let
1919+ osConf = lib.attrByPath host.intoAttr null config.flake;
2020+ vmRunner = osConf.config.microvm.declaredRunner or null;
2121+ package = lib.optionalAttrs (vmRunner != null) {
2222+ ${host.system}.${host.name} = vmRunner;
2323+ };
2424+ in
2525+ package
2626+ ))
2727+ ];
2828+in
2929+{
3030+ imports = [ packagesModule ];
3131+ flake.packages = lib.mkMerge microvmRunners;
3232+}
+54
templates/microvm/modules/runnable-example.nix
···11+# Copied from MicroVM flake template.
22+#
33+# This is just a normal NixOS configuration that happes to include microvm.nix module.
44+# No special Den classes nor context pipeline here. It's all just a single NixOS conf.
55+# Den support: ./microvm-runners.nix
66+#
77+{
88+ inputs,
99+ den,
1010+ lib,
1111+ ...
1212+}:
1313+{
1414+1515+ den.hosts.x86_64-linux.runnable-microvm = {
1616+ intoAttr = [
1717+ "microvms"
1818+ "runnable-microvm"
1919+ ]; # example not intended to be used from nixosConfigurations
2020+ };
2121+2222+ den.aspects.runnable-microvm = {
2323+ nixos = {
2424+ imports = [ inputs.microvm.nixosModules.microvm ];
2525+ users.users.root.password = "";
2626+2727+ # There's not much need to have a forwarding microvm class for runnable vms
2828+ microvm = {
2929+ volumes = [
3030+ {
3131+ mountPoint = "/var";
3232+ image = "var.img";
3333+ size = 256;
3434+ }
3535+ ];
3636+ shares = [
3737+ {
3838+ # use proto = "virtiofs" for MicroVMs that are started by systemd
3939+ proto = "9p";
4040+ tag = "ro-store";
4141+ # a host's /nix/store will be picked up so that no
4242+ # squashfs/erofs will be built for it.
4343+ source = "/nix/store";
4444+ mountPoint = "/nix/.ro-store";
4545+ }
4646+ ];
4747+4848+ # "qemu" has 9p built-in!
4949+ hypervisor = "qemu";
5050+ socket = "control.socket";
5151+ };
5252+ };
5353+ };
5454+}