NixOS Architecture Note: Declarative System Design and Operational Boundaries
How to decide if NixOS fits your infrastructure, where trust boundaries sit, and how to check generations and garbage collection.
20 Oct 2025, 05:32 UTC

The problem this design addresses
Imperative Linux servers accumulate drift. A package installed by hand, a configuration file edited directly, or a kernel parameter changed at runtime is not captured in a single source of truth. Rebuilding a replacement server means guessing what changed. NixOS treats the entire operating system as a function of a declarative configuration: given the same input, it produces the same system state.
Takeaway: use NixOS when reproducibility and atomic rollback matter more than immediate compatibility with pre-compiled binaries or a shallow learning curve.
Requirements
- Rebuild any system from one configuration file.
- Apply changes atomically while keeping a bootable previous state.
- Know exactly which package versions are installed.
- Separate system packages from mutable user data.
Smallest suitable design
A single NixOS host starts with one file, typically /etc/nixos/configuration.nix. It declares the bootloader, filesystems, users, services, and packages. Applying it requires root:
sudo nixos-rebuild switch
This command evaluates the configuration, builds the new system in the Nix store, activates it, and adds a bootloader entry. The previous generation remains available.
{ config, pkgs, ... }:
{
boot.loader.systemd-boot.enable = true;
networking.hostName = "nixos-host-01";
networking.networkmanager.enable = true;
users.users.admin = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" ];
};
environment.systemPackages = with pkgs; [
vim
git
curl
];
services.openssh.enable = true;
}
The Nix store is /nix/store. Each package lives in a directory named with a hash of its inputs, such as /nix/store/v5...-vim-9.0. This makes the store immutable and content-addressable: different versions of the same package can coexist without overwriting each other.
Trust and data boundaries
The store is world-readable by design. Do not place secrets—private keys, database passwords, or tokens—in configuration.nix or any file that ends up in /nix/store. Secrets must be injected at activation time through a mechanism that keeps them out of the store, such as age-encrypted secrets, sops-nix, or systemd credentials.
User data and mutable state belong outside /nix/store: /home, /var/lib, and runtime state in /etc that NixOS does not manage. The boundary is between the immutable, reproducible system definition and the ephemeral, host-specific data that changes after deployment.
Operational checks
Before applying a change:
sudo nixos-rebuild switch --dry-run --show-trace
This evaluates the configuration and reports errors without changing the system. After applying:
sudo nixos-rebuild list-generations
Lists available system generations with dates and metadata. To reclaim disk space:
sudo nix-collect-garbage -d
This removes old generations and unreferenced store paths. Run it only after confirming the current generation boots correctly, because it deletes the rollback target.
To verify rollback, reboot and select the previous generation from the bootloader menu. On systems using systemd-boot, entries appear at boot; on GRUB, they appear in the menu. Do not assume the bootloader is configured until you see the entries.
Failure modes
- Disk exhaustion. Keeping every generation consumes space. Schedule garbage collection or limit retained generations.
- Unbootable configuration. A syntax error or bad kernel parameter can prevent boot. The previous generation is the recovery path, but only if the bootloader still offers it.
- Non-FHS binaries. Pre-compiled software expecting libraries in
/libor/usr/libfails because NixOS does not use the standard filesystem hierarchy. Workarounds such aspatchelforsteam-runadd operational complexity. - Secret leakage. A password committed to
configuration.nixis stored under/nix/storeand in the Nix history. Rotate it immediately if it is committed.
Conditions that would change the design
- Your team runs proprietary software with hardcoded
/usr/libpaths and cannot patch it. NixOS becomes a compatibility tax; consider a traditional distribution with configuration management instead. - You manage many identical hosts. A modular configuration library reduces repetition, but requires learning the Nix expression language.
- Operators cannot read Nix code. If incident response requires editing configuration during an outage, the language barrier is a real operational risk.
Verification
Apply a change and confirm a new generation exists:
sudo nixos-rebuild switch
sudo nixos-rebuild list-generations
Check that the new generation appears and that the system remains reachable. Then reboot and select the previous generation to confirm rollback. If rollback works, the design is safe to iterate on.
Limitations
The Nix expression language has a steep learning curve compared to YAML or JSON. The non-standard filesystem hierarchy breaks assumptions in some third-party installers. Old generations must be garbage collected or disk usage grows unbounded.
Rollback
Because nixos-rebuild switch changes system state, rollback is supported by selecting the previous generation at boot or running sudo nixos-rebuild switch --rollback. Only use rollback after a failed change; it does not restore user data modified after the previous generation was created.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.