Adding a Host
Table of Contents
Adding a machine to an emanix fleet is a machine file, a username, and a build. The build is the point: it either evaluates or it does not, and you find out before touching the machine.
1. 1. Declare the host
In flake.nix, add an entry to nixosConfigurations. The template's
mkHost call is the shape to copy:
<host> = mkHost {
hostName = "<host>";
username = "<you>";
role = "workstation"; # a LABEL — it selects nothing
hardware = ./hardware/<machine>.nix;
extraModules = [
disko.nixosModules.disko
(emanix.lib.mkDisk {
inherit (host) device luks filesystem swapSize;
})
{ emanix.hardware.gpu = host.gpu; }
./configuration.nix
emanix.nixosModules.ewm # only if you want the compositor
];
homeModules = [ ./home/<you>.nix ];
};
A WSL host takes no hardware argument — the WSL module supplies boot and
mounts.
Note what is not implied by role. It imports nothing. If this host should
run the compositor, you import nixosModules.ewm yourself, as above; if it
needs audio, printing or containers, those are your modules too. The
distribution ships one shape, and you compose the rest — see
Architecture.
2. 2. Write the hardware module
Only genuine device facts: initrd modules, firmware, power management. Keep audio, Bluetooth, touchpad and the bootloader out of it — those are not device identity, they are choices about how you want the machine to behave, and they belong in your own modules alongside everything else about this host.
If the machine has a GPU the compositor depends on, set gpu in the machine
file. It forces the driver into the initrd, because bringing it up after
userspace starts loses the display-master race against a compositor launched
from a TTY. Setting it asserts that hardware.enableRedistributableFirmware is
true — the driver is useless without its firmware blobs, and
boot.initrd.allowMissingModules would hide that at build time. Leave it null
on a headless or virtualised host.
3. 3. Describe the disk
Disk layout is declarative and parameterized: the machine file carries the four
values, and lib.mkDisk builds the layout from them.
{
device = "/dev/nvme0n1";
luks = true;
filesystem = "btrfs";
swapSize = "0";
}
Do not also hand-write fileSystems or the LUKS devices in the hardware
module. mkDisk generates them; a second definition collides and the whole
configuration fails to evaluate. That failure is the guardrail working.
4. 4. Create the host anchor
hosts/<host>/configuration.nix holds what is unique to this machine — and
system.stateVersion.
Set stateVersion to the release you are installing now, and never touch it
again. It must not be shared between hosts and must not be bumped on an
existing one. It is not a version to keep current: it is a historical fact
about the install, and changing it silently changes migration behaviour.
5. 5. Verify before you touch the machine
nix flake check
nix build .#nixosConfigurations.<host>.config.system.build.toplevel
Neither needs root, and neither needs the target machine.
Do not build another host's closure on an unrelated machine unless you know its inputs are cached. Source-compiling a desktop's package set on a small virtualised host can exhaust its memory and take the whole environment down. When all you need is a no-op proof, compare derivation paths instead — this is cheap, pure evaluation, and it answers "did my refactor change anything?" exactly:
nix eval --raw .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath
Identical paths before and after mean the change is provably inert. Differing paths tell you to go find out why — and the answer is sometimes legitimate, like an error message that now names a renamed option.
6. 6. Install and join
Follow Installation. emanix-firstboot runs
whatever you put in emanix.firstboot.text — typically joining a network,
pairing file sync, cloning your flake and confirming secrets decrypt. The
distribution supplies the convention and the build-time shellcheck; the steps
are yours.
7. Adapting emanix rather than adopting it
This split used to need a warning. It no longer does, because the split is now the repository boundary: everything general is in Emanix, and everything personal is in the flake that consumes it. The two-tier module layout, the theme system, the compositor and the host composer work for anyone. The shell aliases, the agent, the git identity and the network enrolment are not in here to inherit.
The honest summary is that Emanix is public-shaped rather than a product. It was written with an outside reader in mind, which is why the seams are documented and why the personal half was moved out — but it is still one person's machine, and the interesting thing to take from it is the boundary, not the package list.