Skip to content

VM Helper Functions

The lib.ghaf.vm namespace provides utilities for VM composition, configuration propagation, and evaluation within the Ghaf framework.

lib.ghaf.vm = {
mkSpecialArgs # Create specialArgs for VM evaluation
mkHostConfig # Create host-derived configuration for a VM
getConfig # Get inner NixOS config from a microvm entry
applyVmConfig # Build modules list from vmConfig for extendModules
};

Creates the specialArgs attribute set passed to VM module evaluation. This makes globalConfig, hostConfig, inputs, and lib available to all VM modules.

Signature:

mkSpecialArgs :: { lib, inputs, globalConfig, hostConfig?, extraArgs? } -> AttrSet

Parameters:

NameTypeRequiredDescription
libAttrSetYesExtended lib with ghaf functions
inputsAttrSetYesFlake inputs
globalConfigAttrSetYesGlobal config value (from config.ghaf.global-config)
hostConfigAttrSetNoHost-specific config (from lib.ghaf.vm.mkHostConfig)
extraArgsAttrSetNoAdditional specialArgs to include

Returns: AttrSet suitable for specialArgs in lib.nixosSystem or extendModules.

Example:

let
specialArgs = lib.ghaf.vm.mkSpecialArgs {
inherit lib inputs;
globalConfig = config.ghaf.global-config;
hostConfig = lib.ghaf.vm.mkHostConfig {
inherit config;
vmName = "gui-vm";
};
};
in
lib.nixosSystem {
inherit specialArgs;
modules = [ ./guivm-base.nix ];
}

Creates the host-derived configuration for a specific VM. This extracts VM-specific information (networking, kernel, hardware passthrough, user config) from the host configuration.

Signature:

mkHostConfig :: { config, vmName, extraConfig? } -> AttrSet

Parameters:

NameTypeRequiredDescription
configAttrSetYesFull host NixOS configuration
vmNameStringYesName of the VM (e.g., “gui-vm”, “net-vm”)
extraConfigAttrSetNoAdditional config to merge

Returns: AttrSet with VM-specific host-derived values.

Example:

hostConfig = lib.ghaf.vm.mkHostConfig {
inherit config;
vmName = "gui-vm";
};

Returned Structure:

{
vmName = "gui-vm";
vmType = "gui"; # vmName without "-vm" suffix
kernel = { ... }; # ghaf.kernel.<vmType> settings
qemu = { ... }; # ghaf.qemu.<vmType> settings
passthrough = {
qemuExtraArgs = [ ... ]; # Hardware passthrough args
vmUdevExtraRules = "..."; # Udev rules for passthrough
};
sharedVmDirectory = "/..."; # Shared directory path
microvmBoot = {
enable = bool; # microvm-boot enabled
};
hardware = {
devices = { ... }; # Hardware device definitions
};
users = { ... }; # User configuration
common = { ... }; # Common namespace settings
networking = {
hosts = { ... }; # All VM host entries
thisVm = { ... }; # This VM's network config
};
givc = {
cliArgs = "..."; # GIVC CLI arguments
enableTls = bool; # TLS enabled
};
security = {
sshKeys = { ... }; # SSH key configuration
};
appvms = { ... }; # AppVM configurations (for launcher)
guivm = {
applications = [ ... ]; # GUI VM applications
};
reference = {
services = { ... }; # Reference services config
};
}

Retrieves the inner NixOS config from a microvm.vms entry. Handles both evaluatedConfig and legacy config patterns.

Signature:

getConfig :: vmEntry -> AttrSet | null

Parameters:

NameTypeDescription
vmEntryAttrSetEntry from config.microvm.vms.<name>

Returns: The VM’s inner NixOS config, or null if not available.

Example:

# Get GUI VM's inner config
let
guivmEntry = config.microvm.vms.gui-vm;
guivmConfig = lib.ghaf.vm.getConfig guivmEntry;
in
guivmConfig.services.foo.enable

Notes:

  • Prefers evaluatedConfig over legacy config pattern
  • Returns vmEntry.evaluatedConfig.config if evaluatedConfig is set
  • Falls back to vmEntry.config.config for legacy VMs
  • Returns null if neither is available

Builds a list of modules from ghaf.hardware.definition and ghaf.virtualization.vmConfig for use with extendModules. This applies resource allocation (mem/vcpu) and extra modules.

Signature:

applyVmConfig :: { config, vmName } -> [ Module ]

Parameters:

NameTypeDescription
configAttrSetHost NixOS configuration
vmNameStringVM name without -vm suffix (e.g., “guivm”, “netvm”)

Returns: List of modules to pass to extendModules.

Module Merge Order (highest priority last):

  1. Base module (e.g., guivm-base.nix) - sets mkDefault values
  2. resourceModule - applies vmConfig.mem/vcpu
  3. hwModules - hardware.definition.<vm>.extraModules
  4. vmConfigModules - vmConfig.<vm>.extraModules (highest priority)

Example:

# In a profile module
guivmBase.extendModules {
modules = lib.ghaf.vm.applyVmConfig {
inherit config;
vmName = "guivm";
};
};

What it returns:

# If vmConfig.guivm = { mem = 4096; vcpu = 4; extraModules = [ myModule ]; }
# And hardware.definition.guivm = { extraModules = [ hwModule ]; }
# Returns:
[
{ microvm.mem = 4096; microvm.vcpu = 4; } # resourceModule
hwModule # from hardware.definition
myModule # from vmConfig
]

Pattern 1: Creating a VM Base in a Profile

Section titled “Pattern 1: Creating a VM Base in a Profile”

Profiles create VM bases and export them for downstream extension. The profile also applies vmConfig (resource allocation, hardware modules) and sets evaluatedConfig on the VM module. Targets and references may override evaluatedConfig via extendModules.

modules/profiles/laptop-x86.nix
{ config, lib, pkgs, inputs, ... }:
let
hostGlobalConfig = config.ghaf.global-config;
guivmBase = lib.nixosSystem {
specialArgs = lib.ghaf.vm.mkSpecialArgs {
inherit lib inputs;
globalConfig = hostGlobalConfig;
hostConfig = lib.ghaf.vm.mkHostConfig {
inherit config;
vmName = "gui-vm";
};
};
modules = [
inputs.self.nixosModules.guivm-base
# Base configuration here
];
};
in
{
# Export for downstream extension
options.ghaf.profiles.laptop-x86.guivmBase = lib.mkOption {
type = lib.types.unspecified;
default = guivmBase;
description = "GUI VM base configuration";
};
# Set evaluatedConfig with vmConfig applied
# Pass the full system result (not .config) to evaluatedConfig
config.ghaf.virtualization.microvm.guivm.evaluatedConfig =
guivmBase.extendModules {
modules = lib.ghaf.vm.applyVmConfig {
inherit config;
vmName = "guivm";
};
};
}

Pattern 2: Downstream Extension with extendModules

Section titled “Pattern 2: Downstream Extension with extendModules”
# In downstream project (e.g., ghaf-fmo-laptop)
{ config, lib, inputs, ... }:
let
hostGlobalConfig = config.ghaf.global-config;
in
{
# Extend the upstream guivmBase
ghaf.virtualization.microvm.guivm.evaluatedConfig =
config.ghaf.profiles.laptop-x86.guivmBase.extendModules {
modules = [
# Add FMO-specific modules
../services
../programs
{ ghaf.reference.personalize.keys.enable = true; }
]
# Apply vmConfig (mem, vcpu, extraModules)
++ lib.ghaf.vm.applyVmConfig {
inherit config;
vmName = "guivm";
};
# Update specialArgs for extended modules
specialArgs = lib.ghaf.vm.mkSpecialArgs {
inherit lib inputs;
globalConfig = hostGlobalConfig;
hostConfig = lib.ghaf.vm.mkHostConfig {
inherit config;
vmName = "gui-vm";
};
};
};
}
# Using the extensions option for feature modules
{ config, lib, pkgs, ... }:
{
ghaf.virtualization.microvm.appvm.vms.chrome.extensions = [
({ pkgs, ... }: {
ghaf.appvm.applications = [{
name = "Getting Started";
description = "Introduction guide";
packages = [ pkgs.ghaf-intro ];
command = "ghaf-intro";
}];
})
];
}