Skip to content

Template: MicroVM

templates/microvm demonstrates Den’s extensibility: custom den.schema and den.policies extensions for integrating other Nix libraries like MicroVM.nix.

This template shows two patterns for building MicroVMs with Den.

A standalone NixOS configuration that runs as MicroVM directly as an application.

Terminal window
nix run .#runnable-microvm

See MicroVM Docs on Package Runners

Guest MicroVMs declared on a host and managed together.

Terminal window
nixos-rebuild build --flake .#server

See MicroVM Docs on Declarative MicroVMs

Terminal window
mkdir my-nix && cd my-nix
nix flake init -t github:denful/den#microvm
nix flake update den
flake.nix
modules/
den.nix # Enable Den + hostname for all hosts
runnable-example.nix # Standalone NixOS MicroVM
microvm-runners.nix # Auto-expose runnable VMs as flake packages
guests-example.nix # Server host with declarative guest VMs
microvm-integration.nix # Schema extensions + context pipelines

Any runnable MicroVM with declaredRunner is auto-exposed as a flake package:

modules/microvm-runners.nix
flake.packages = {
x86_64-linux.runnable-microvm = <runner>;
};

microvm-integration.nix extends den.schema.host with MicroVM-specific options via den.schema.host.imports:

options.microvm.guests = lib.mkOption {
type = lib.types.listOf lib.types.raw;
default = [ ];
description = "Guest MicroVMs. A list of Den hosts: [ den.hosts.x86_64-linux.foo-microvm ]";
};
options.microvm.sharedNixStore = lib.mkEnableOption "Auto share nix store from host";
modules/guests-example.nix
# Server host with guest VM
den.hosts.x86_64-linux.server.microvm.guests = [
den.hosts.x86_64-linux.guest-microvm
];
# Guest VM declaration (no top-level nixosConfiguration)
den.hosts.x86_64-linux.guest-microvm.intoAttr = [];
# Server config
den.aspects.server = {
nixos.microvm.host.startupTimeout = 300;
};
den.aspects.guest-microvm = {
# Den resolved and forwarded to `<host>.microvm.vms.guest-microvm.config`
nixos = { pkgs, ... }: {
environment.systemPackages = [ pkgs.cowsay ];
};
# Forwarded to `<host>.microvm.vms.guest-microvm`
microvm.autostart = true;
};

The guest integration registers a custom microvm class and wires three policies into the schema. Effect constructors come from den.lib.policy:

inherit (den.lib.policy) resolve include provide;
# Register the microvm class so guest aspects can emit `microvm` keys.
den.classes.microvm.description = "MicroVM guest configuration (microvm.nix options)";

Stage 1 — when a host declares guests, branch into a microvm-host context and import the host.nix module inline:

den.policies.host-to-microvm-host =
{ host, ... }:
lib.optionals (host.microvm.guests != [ ]) [
(resolve.to "microvm-host" { inherit host; })
(include (
{ host }:
{
${host.class}.imports = [ host.microvm.hostModule ];
}
))
];

Stage 2 — fan out one microvm-guest context per declared guest:

den.policies.microvm-host-to-microvm-guest =
{ host, ... }:
lib.concatMap (vm: [
(resolve.to "microvm-guest" { inherit host vm; })
]) host.microvm.guests;

Stage 3 — resolve each guest as an isolated host pipeline, then provide its modules to the server at the right microvm.vms.<name> paths:

den.policies.microvm-guest-resolve-vm =
{ host, vm, ... }:
let
# Resolve VM as an isolated host pipeline — its modules stay external.
vmResolved = den.lib.aspects.resolve vm.class (den.lib.resolveEntity "host" { host = vm; });
microvmResolved = den.lib.aspects.resolve "microvm" vm.aspect;
in
[
# Deliver VM's OS class modules to server at microvm.vms.<name>.config
(provide {
class = host.class;
path = [ "microvm" "vms" vm.name "config" ];
module = _: vmResolved;
})
# Deliver VM's microvm class modules to server at microvm.vms.<name>
(provide {
class = host.class;
path = [ "microvm" "vms" vm.name ];
module = _: microvmResolved;
})
];

The policies are attached to their stages through the schema:

den.schema.host.includes = [ den.policies.host-to-microvm-host ];
den.schema.microvm-host.includes = [ den.policies.microvm-host-to-microvm-guest ];
den.schema.microvm-guest.includes = [ den.policies.microvm-guest-resolve-vm ];
graph TD
    D1["server host"]
    D2["host stage<br/>normal pipeline"]
    D3["server nixos config"]
    D5["host.microvm.host<br/>loaded"]
    
    D6["guest-microvm host"]
    D7["host stage<br/>NORMAL PIPELINE"]
    D8["guest nixos config<br/>+ microvm class<br/>all Den features supported"]
    
    D9["forward to host:<br/>microvm.vms.guest-microvm.config"]
    D10["forward to host:<br/>microvm.vms.guest-microvm"]
    D11["server nixosConfiguration"]

    D1 -->|Den transforms to| D2
    D2 -->|microvm.guests != empty<br/>triggers microvm-host| D5
    D2 -->|produces| D3
    
    D6 -->|Uses normal Den pipeline<br/>host stage host=guest| D7
    D7 -->|produces| D8
    
    D8 -->|forward nixos| D9
    D8 -->|forward microvm| D10
    D3 --> D11
    D5 --> D11
    D9 --> D11
    D10 --> D11
FeatureProvided
Custom context pipelines
Schema extensions
Forward providers
Standalone runnable VMs
Host-guest architecture
Declarative MicroVM
Auto nix store sharing
Contribute Community Sponsor