Many hosts, one base
Most flakes with more than one machine grow a mkHost function. It takes a host name, imports a shared module
and ./hosts/${name}, sets networking.hostName, and passes values in specialArgs. When there are many hosts,
a vars.nix file merges defaults, then category values, then host values. Tools such as
easy-hosts add modules per class, per arch and per tag.
# flake.nix: the usual shapenixosConfigurations = lib.genAttrs (builtins.attrNames (builtins.readDir ./hosts)) ( name: nixpkgs.lib.nixosSystem { specialArgs = { inherit inputs; hostname = name; vars = getHostVars name; }; modules = [ ./common.nix ./hosts/${name} { networking.hostName = name; } ]; });In yen a host is an instance of a kind. The kind builds the NixOS system once. Shared configuration is an aspect at the kind. Host values are typed fields with defaults, and every NixOS module of an aspect can read them as an arg. The files below show each part. A table after them maps each usual pattern to its yen form.
The kind
Section titled “The kind”# Hosts are data. One kind builds every NixOS system: no mkHost function, no readDir, no specialArgs.{ yen, lib, inputs, ...}:let inherit (yen.lib) opt;in{ yen.kinds.fleet = { spawns.host = _: yen.host; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.host = { # what a vars.nix file held: typed, with defaults fields = { # (1) system = opt (yen.lib.enum [ "x86_64-linux" "aarch64-linux" ]) "x86_64-linux"; laptop = opt yen.lib.bool false; }; sinks.nixos.out = # (2) { host, nixos, ... }: { nixosConfigurations.${host.name} = inputs.nixpkgs.lib.nixosSystem { modules = nixos; }; }; };
yen.fleet.main = { }; yen.flake = { out, ... }: out;}(1) is what a vars.nix file held: typed fields with defaults. (2) is the nixos sink:
it builds one NixOS system for each host.
A wrong value fails at the instance that sets it, not deep in nixpkgs.hostPlatform. With
yen.host.tower.system = "x86-64-linux", the result has this error, as data:
[ { class = "fixable"; code = "check"; error = "system fails its check"; fix = "give system a value of type one of \"x86_64-linux\", \"aarch64-linux\""; from = "host:tower"; got = "x86-64-linux"; path = [ "system" ]; want = "one of \"x86_64-linux\", \"aarch64-linux\""; }]The base, per arch, per form
Section titled “The base, per arch, per form”# What mkHost gave every host, and what easy-hosts gives per arch or per class: aspects at the host kind.{ yen, ... }:{ yen.aspects.base = { at = yen.kinds.host; # (1) nixos = { host, ... }: { networking.hostName = host.name; # (2) no specialArgs.hostname: the host is an arg nixpkgs.hostPlatform = host.system; nix.settings.experimental-features = [ "nix-command" "flakes" ]; system.stateVersion = "25.05"; }; }; # per arch: `at` reads a field yen.aspects.arm = { at = s: (s.system or null) == "aarch64-linux"; # (3) nixos.boot.loader.generic-extlinux-compatible.enable = true; }; yen.aspects.laptop = { at = s: s.laptop or false; nixos.services.upower.enable = true; };}(1) makes base active at every host. (2) reads the host through the host arg. (3) makes arm active
where the system field is aarch64-linux. See Predicates.
One file per host
Section titled “One file per host”# One file per host: the instance and its own hardware. A new file is a new host.{ yen, ... }:{ yen.host.tower = { }; # (1) yen.aspects.tower = { at = yen.host.tower; # (2) nixos = { boot.loader.systemd-boot.enable = true; fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; }; }; };}{ yen, ... }:{ yen.host.thinkpad.laptop = true; yen.aspects.thinkpad = { at = yen.host.thinkpad; nixos = { boot.loader.systemd-boot.enable = true; fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext4"; }; }; };}{ yen, ... }:{ yen.host.pi.system = "aarch64-linux"; yen.aspects.pi = { at = yen.host.pi; nixos.fileSystems."/" = { device = "/dev/disk/by-label/NIXOS_SD"; fsType = "ext4"; }; };}In tower.nix, (1) declares the instance and (2) is the aspect for this host only. A real host file
usually imports its hardware-configuration.nix in its nixos key, next to the instance.
The usual patterns in yen
Section titled “The usual patterns in yen”| Usual pattern | yen |
|---|---|
mkHost "tower" |
yen.host.tower = { } |
builtins.readDir ./hosts |
import-tree: each file under modules/hosts/ declares its host |
specialArgs.hostname, specialArgs.vars |
{ host, ... }: in the NixOS module of any aspect |
vars.nix: defaults, category, host |
fields with defaults; for layered values see Environments |
easy-hosts perArch, perClass, perTag |
an aspect whose at reads a field; see Roles from data |
nixos-rebuild switch --flake .#tower |
the same: the output name is the instance name |