Declarative Configuration, Store, and Generations
- Type: Tutorial
- Status: Current
- Scope: How a
nixos-configdeclaration becomes a build, active system, and rollback generation- Canonical sources:
flake.nix;justfiles/deploy.just;modules/maintenance/garbage-collection.nix;modules/services/backrest.nix; Safe Testing and Deployment; Full-Host Recovery; NixOS manual- Last verified: Source commit
556df88494686003b1c4f20c8e0b99b5afc16a6e(2026-09-12); source HEADb2831d33132967b4c4fb69e6068b2b05fff8fdffchanges onlyflake.lock. Builder, validation/activation recipes, and GC policy checked 2026-09-13; no build or activation performed.- Review triggers: Flake builder, deployment recipes, garbage-collection policy, state/backup architecture, or upstream NixOS generation semantics
Learning goal and prerequisites
Distinguish source, evaluation, build, activation, a NixOS generation, and mutable application data. Start with the Guided Repository Tour and Nix Language Essentials. The official NixOS manual is the general reference; this lesson describes how the lifecycle appears in this fleet.
One change, several distinct states
Suppose a host changes an option in hosts/vega/default.nix or an imported
service module. The .nix files declare intended configuration; they do
not mutate Vega merely by being edited. flake.nix selects the host modules
and pinned inputs, then Nix evaluates the merged options. A build realises
a host closure—the system and its referenced store paths—without changing
the running machine. This project uses:
just check
nix build .#nixosConfigurations.vega.config.system.build.toplevel --no-link
just check runs nix flake check, including the repository's formatting
and updater state-machine checks. The explicit build is stronger for Vega:
it constructs that host top-level, but still does not deploy it. Stage new
.nix or encrypted secret files before either command because Git-backed
flakes omit untracked files. Testing and CI
Contracts explains the full gate selection.
The Nix store holds immutable build outputs under /nix/store. Multiple
versions can coexist, and a system profile points to the chosen closure.
just test builds and activates the current host without making it the next
boot default; just deploy performs the reviewed deployment path. Electra's
recipes preserve its current physical-hardware specialisation unless an
explicit, valid mode change is requested. Never substitute generic
nixos-rebuild test commands for the project wrapper when testing Electra.
Use Safe Testing and Deployment for holds,
canary and rollback operations rather than running activation as an exercise.
A successful switch creates a NixOS system generation, so a previous
configuration can often be selected at boot or reactivated. That is a
rollback of the declared system closure, not time travel for every disk.
modules/maintenance/garbage-collection.nix schedules daily GC with
--delete-older-than 7d; old generations are a finite rollback resource,
not an indefinite archive. just generations displays recent system
generations, and just build-diff compares a proposed build with the
running system.
What the declaration does not capture
The pinned flake.lock and source are necessary for repeatable builds, but
they do not contain live PostgreSQL/MariaDB databases, mutable web UI
settings, container volumes, NAS contents, SOPS plaintext, or externally
managed DNS/ingress. Even the same closure can behave differently if a mount
is absent, a credential has rotated, or a service has migrated its schema.
The Storage and Data Map and Network and Access
Topology identify these boundaries.
Generation rollback may fail to recover an application after a database schema upgrade; it cannot undo a Disko format or recreate missing data. A stateful service needs an application-consistent backup and tested restore. Follow Backup Verification and Partial Restore or Full-Host Recovery for real recovery, not this tutorial.
Guided check
Without activating anything, find system.configurationRevision in
flake.nix and explain which source revision a clean build records and why
a dirty build is labelled differently. Then find the GC options in
modules/maintenance/garbage-collection.nix. Answer: if you deploy a
new Jellyfin module but its database is independently upgraded, which part
can a previous NixOS generation restore and which part requires a data
recovery plan? Verify your answer against Service Migration and
Cutover.
Common mistakes: treating nix flake check as a host deployment, treating
a successful host build as proof of live service health, treating a
generation as a database snapshot, and assuming removing an option erases
all mutable state. The next lesson, Flakes and Inputs, examines how the
source and pinned dependencies enter evaluation.