Skip to content

Extending Targets

This guide covers how to customize existing Ghaf hardware targets, from simple configuration changes to creating entirely new variants.

Targets in Ghaf combine:

  1. Hardware Definition - Device-specific settings (modules/hardware/)
  2. Profile - Platform capabilities (modules/profiles/)
  3. Variant - Debug/release configuration (lib/ghaf.profiles.*)
  4. Custom Modules - Target-specific additions

Override global config settings via extraConfig:

# In targets/laptop/flake-module.nix
mkGhafConfiguration {
name = "my-target";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-target;
variant = "debug";
# Override specific global config values
extraConfig = {
global-config.features.bluetooth.enable = false;
global-config.logging.listener.port = 10000;
};
extraModules = [
# Additional modules
];
};

Add packages to a target:

extraModules = [
({ pkgs, ... }: {
environment.systemPackages = with pkgs; [
vim
htop
tmux
];
})
];

Enable host services:

extraModules = [
{
services.tailscale.enable = true;
networking.firewall.allowedUDPPorts = [ 41641 ];
}
];

Use extendModules to customize VMs:

# In a target or custom profile
{ config, lib, ... }:
let
baseGuivm = config.ghaf.profiles.laptop-x86.guivmBase;
customGuivm = baseGuivm.extendModules {
modules = [
({ pkgs, ... }: {
# Add applications
environment.systemPackages = [ pkgs.chromium ];
# Configure desktop
services.xserver.desktopManager.gnome.enable = true;
})
];
};
in
{
ghaf.virtualization.microvm.guivm.evaluatedConfig = customGuivm;
}

Add custom application VMs:

{ config, ... }:
let
mkAppVm = config.ghaf.profiles.laptop-x86.mkAppVm;
in
{
ghaf.virtualization.microvm.appvm.vms.my-custom-app = mkAppVm {
name = "my-custom-app";
applications = [{
name = "My Application";
description = "Custom application for this target";
packages = [ pkgs.my-app ];
icon = "my-app";
command = "my-app";
}];
};
}

Move hardware features to different VMs:

{
ghaf.global-config.features = {
# Move fingerprint to admin VM
fprint.targetVms = [ "admin-vm" ];
# Enable YubiKey in multiple VMs
yubikey.targetVms = [ "gui-vm" "admin-vm" ];
# Disable Bluetooth entirely
bluetooth.enable = false;
};
}

Create both debug and release variants:

# In flake-module.nix
let
mkGhafConfiguration = inputs.self.lib.ghaf.builders.mkGhafConfiguration;
in
{
flake.nixosConfigurations = {
# Debug variant
"my-target-debug" = mkGhafConfiguration {
name = "my-target";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-target;
variant = "debug";
};
# Release variant
"my-target-release" = mkGhafConfiguration {
name = "my-target";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-target;
variant = "release";
};
};
}

Create custom profile combinations by overriding global config:

{
# Minimal debug - debug tools but reduced features
"my-target-minimal-debug" = mkGhafConfiguration {
name = "my-target-minimal";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-target;
variant = "debug";
extraConfig = {
global-config = {
givc.enable = false;
features.audio.enable = false;
features.bluetooth.enable = false;
};
};
};
}

Create modules/hardware/my-device/definition.nix:

Apache-2.0
# SPDX-FileCopyrightText: 2022-2026 TII (SSRC) and the Ghaf contributors
{ config, lib, ... }:
{
_file = ./definition.nix;
hardware.definition = {
name = "My Device";
skuName = "my-device";
host.kernelConfig.kernelParams = [ "quiet" ];
network.pciDevices = [{
path = "0000:00:14.3";
vendorId = "8086";
productId = "a0f0";
name = "wlp0s20f3";
}];
gpu.pciDevices = [{
path = "0000:00:02.0";
vendorId = "8086";
productId = "9a49";
name = "Intel Graphics";
}];
# VM-specific hardware
guivm = {
extraModules = [];
};
netvm = {
extraModules = [];
};
};
}

Create modules/hardware/my-device/default.nix:

Apache-2.0
# SPDX-FileCopyrightText: 2022-2026 TII (SSRC) and the Ghaf contributors
{ config, lib, pkgs, ... }:
{
_file = ./default.nix;
imports = [
./definition.nix
];
# Device-specific kernel
boot.kernelPackages = pkgs.linuxPackages_latest;
# Firmware
hardware.firmware = [ pkgs.linux-firmware ];
# Device-specific settings
hardware.enableRedistributableFirmware = true;
}

Create targets/my-device/flake-module.nix:

Apache-2.0
# SPDX-FileCopyrightText: 2022-2026 TII (SSRC) and the Ghaf contributors
{
inputs,
lib,
...
}:
let
mkGhafConfiguration = inputs.self.lib.ghaf.builders.mkGhafConfiguration;
in
{
flake.nixosConfigurations = {
my-device-debug = mkGhafConfiguration {
name = "my-device";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-device;
variant = "debug";
extraModules = [
# Target-specific additions
];
};
my-device-release = mkGhafConfiguration {
name = "my-device";
system = "x86_64-linux";
profile = "laptop-x86";
hardwareModule = inputs.self.nixosModules.hardware-my-device;
variant = "release";
};
};
}

Add to modules/hardware/flake-module.nix:

{
flake.nixosModules = {
hardware-my-device = ./my-device;
};
}

mkGhafConfiguration {
# ... other params ...
vmConfig = {
guivm = { mem = 8192; vcpu = 8; };
netvm = { mem = 1024; vcpu = 2; };
audiovm = { mem = 512; vcpu = 1; };
};
}
{
ghaf.common.extraNetworking = {
hosts = {
gui-vm = { ipv4 = "10.0.0.2"; mac = "02:00:00:00:00:02"; };
net-vm = { ipv4 = "10.0.0.3"; mac = "02:00:00:00:00:03"; };
};
hostAddress = "10.0.0.1";
networkPrefix = "10.0.0.0/24";
};
}
extraModules = [
({ config, pkgs, ... }: {
# Touchpad configuration
services.libinput.enable = true;
services.libinput.touchpad = {
tapping = true;
naturalScrolling = true;
};
# Power management
services.tlp.enable = true;
services.thermald.enable = true;
})
];
extraModules = [
({ config, lib, ... }: {
# Only apply if debug profile
config = lib.mkIf config.ghaf.global-config.debug.enable {
services.getty.autologinUser = "ghaf";
};
})
];

Terminal window
# Debug variant
nix build .#my-device-debug
# Release variant
nix build .#my-device-release
Terminal window
nix build .#my-device-debug --dry-run
Terminal window
# Check VM configuration
nix eval .#nixosConfigurations.my-device-debug.config.microvm.vms.gui-vm.config.config.networking.hostName
# Check global config
nix eval .#nixosConfigurations.my-device-debug.config.ghaf.global-config --json | jq

  • Create hardware definition with _file declaration
  • Create hardware module with device-specific settings
  • Export hardware module in flake-module.nix
  • Create target configuration in targets/
  • Configure both debug and release variants
  • Add installer configuration if needed
  • Test build succeeds
  • Test on actual hardware
  • Document hardware requirements