Choose where a system is built
Building a NixOS system needs memory and CPU. The machine that will run the system is often not the best machine to build it — and sometimes cannot build it at all.
deployment.buildOn decides this per host.
The options
buildOn | Evaluates on | Realizes on | Use when |
|---|---|---|---|
auto (default) | resolved | resolved | Let NXD decide from the target and available builders |
builder | builder | builder | A capable machine builds; only the result ships |
local | orchestrator | orchestrator | Your workstation can produce this system |
target | target | target | The target has ample CPU and memory |
instantiated | orchestrator | target | The target cannot evaluate, but should hold the result |
native | target | target | Force target-native even under low memory |
cross | orchestrator | orchestrator | Cross-compile, e.g. ARM64 from x86_64 |
buildOn = "builder" requires deployment.builder = "user@host".
The low-memory case
Evaluation — working out what to build — can require more memory than a small machine has, before a single package is compiled. A 1 GB VM will often be OOM-killed during evaluation.
Set lowMem:
deployment = {
lowMem = "yes";
};
With lowMem = "yes", a target build is automatically upgraded to
instantiated: the orchestrator performs the memory-hungry evaluation, and the
target realizes the reviewed derivation into its own store. The small machine
never holds the build plan, but the finished system is still assembled where it
runs, so no large closure transfer is needed.
NXD validates that the derivation produces the reviewed output, so this is not a weaker guarantee than building anywhere else.
Declare builders explicitly
Use builderBySystem to declare which builder serves which system type:
deployment = {
buildOn = "builder";
builderBySystem = {
"x86_64-linux" = "deploy@utils";
};
};
NXD deliberately does not inherit ambient builder configuration from
/etc/nix/machines. A build that silently used whatever the workstation
happened to have configured would make placement invisible and unreproducible
between operators. Declaring builders in the repository keeps placement part of
reviewed configuration.
Confirming what happened
Provider progress reports both the configured and the resolved placement:
✓ [medo] build placement configured=auto builder=deploy@utils
If auto resolved somewhere unexpected, this line says so.
A note on evaluating locally
Evaluating on your workstation instead of a Linux builder is a mitigation for memory pressure, not a speedup. Local evaluation on macOS in particular is slower than evaluating on a Linux builder. Choose it when the alternative is an OOM kill, not to make deployments faster.