Skip to content

Host Template System

The problem

Three hosts, each historically carrying its own near-identical import list and boilerplate. Adding a module meant editing every host; the lists drifted.

The solution

A single parameterised template at hosts/templates/desktop.nix, surfaced through lib/hostTypes.nix as two entry points:

  • hostTypes.workstation — used by p620 and p510
  • hostTypes.laptop — used by razer

The template takes one argument, profile, and imports the entire module tree. Profile-specific behaviour is applied with lib.mkIf.

{ profile ? "workstation" }:
{ lib, ... }:
{
  imports = [
    ../../modules/core.nix
    ../../modules/development.nix
    ../../modules/desktop.nix
    ../../modules/virtualization.nix
    ../../modules/performance.nix
    ../../modules/email.nix
    ../../modules/cloud.nix
    ../../modules/programs.nix
    ../../modules/common/ai-defaults.nix
    ../../modules/windows/winboat.nix
  ];

  config = lib.mkMerge [
    {
      aiDefaults.enable = lib.mkDefault true;
      services.openssh.enable = lib.mkDefault true;
    }
    (lib.mkIf (profile == "laptop") {
      services.thermald.enable = lib.mkDefault true;
      powerManagement = {
        enable = lib.mkDefault true;
        cpuFreqGovernor = lib.mkDefault "powersave";
      };
    })
  ];
}

How a host wires in

lib/hostTypes.nix wraps the template and layers on sensible feature defaults that a host can still override with mkForce:

workstation = {
  imports = [ (desktopTemplate "workstation") ];
  config = {
    aiDefaults.profile = "workstation";
    features = {
      development.enable = lib.mkDefault true;
      desktop.enable = lib.mkDefault true;
      virtualization.enable = lib.mkDefault true;
    };
  };
};

A host's configuration.nix then imports the host type and declares only what is unique to it.

Why mkDefault everywhere

The template sets defaults, not hard values. A host that needs something different (e.g. p510 disabling Omarchy's desktop preinstalls to save disk) overrides cleanly without fighting the template.

A server from the same template

p510 is the always-on media server, yet it uses the workstation template. Rather than maintain a separate server template (the old server/hybrid/base templates were removed), it sets host.class = "workstation" and overrides only the handful of things that genuinely differ — currently programs.nixarchy.preinstalls = false and autologin on.

It was headless until 2026-08-31, when it gained a monitor and the Omarchy session; the headless-rdp value of host.class remains in the enum but no host uses it. See p510.

This keeps one template instead of three and avoids the divergence that the template system exists to prevent.

Adding a new host

  1. Create hosts/<name>/ with variables.nix and hardware-configuration.nix.
  2. Import the appropriate host type (hostTypes.workstation or hostTypes.laptop).
  3. Declare host-unique bits (GPU profile, monitors, host-specific features).
  4. Register it in flake.nix (hostUsers, hardwareProfiles).
  5. Add the host SSH key to secrets.nix and rekey.

Full procedure: Adding a Host.