Architecting Atomic System Updates with NixOS Declarative Configuration
Learn how NixOS achieves atomic, declarative system updates via a sandboxed build, immutable store paths, and symlink switching, with operational checks and failure modes.
21 Feb 2026, 22:46 UTC

The Problem: Configuration Drift and Failed Updates
Traditional Linux distributions rely on imperative package management. When you run an update or change a configuration file, the system state is modified in-place. If a power failure occurs mid-update, or if a new package introduces a breaking change, the system enters an inconsistent state. Recovering often requires manual intervention via a live USB or complex backup restoration.
The takeaway is that by treating the entire system state as a single, immutable derivation, you can achieve atomic updates: the system either transitions fully to the new state or remains exactly where it was, with no "half-updated" middle ground.
Minimal Design for Atomic State
The smallest suitable design for this behavior is a declarative expression that evaluates to a system derivation. Instead of modifying files in /etc or /usr, NixOS uses the following architecture:
- The Nix Expression: A configuration.nix file defines the desired state (packages, users, services).
- The Sandbox Build: The Nix evaluator builds a closure—a complete set of all required binaries and config files—inside an isolated sandbox. This ensures no undeclared host dependencies leak into the build.
- The Nix Store: The resulting closure is stored in /nix/store under a unique hash. These paths are read-only.
- The Atomic Switch: Activation occurs by updating a symbolic link (symlink) at /run/current-system to point to the new store path. This operation is atomic at the filesystem level.
Trust and Data Boundaries
To maintain reproducibility, NixOS enforces strict boundaries between trusted immutable data and mutable user input.
| Boundary | Trust Level | Role |
|---|---|---|
| /nix/store | Trusted / Read-Only | Contains the verified build output. Once written, it is never modified. |
| /etc/nixos/ | User-Controlled / Mutable | The source of truth (configuration.nix). This is the only place where changes are initiated. |
| Build Sandbox | Isolated | Prevents the build process from accessing network or filesystem resources not explicitly declared. |
Operational Checks and Verification
Verification of the system state is performed by checking the symlink target and the generation history. These commands should be run as a user with sudo privileges.
Validating a New Configuration
To test a change without making it permanent across reboots, use the test command:
sudo nixos-rebuild test
Expected Result: The command completes successfully, and ls -l /run/current-system shows a path pointing to a new hash in /nix/store.
Auditing System Generations
To see the history of atomic states available for rollback:
sudo nix-env --list-generations -p /nix/var/nix/profiles/system
This lists all previous system closures. If a new update causes instability, you can select a previous generation at the GRUB/systemd-boot menu during startup.
Reclaiming Disk Space
Because every update creates a new immutable closure, disk usage grows over time. Use the following to remove old generations:
sudo nix-collect-garbage -d
Risk: This deletes the ability to roll back to any generation older than the current one.
Failure Modes and Limitations
Build-Time Failures
If configuration.nix contains a syntax error or a missing dependency, the nixos-rebuild process will abort. Because the new closure is never fully realized in the store and the /run/current-system symlink is not updated, the running system remains unaffected.
Bootloader Corruption
While the store paths are atomic, the bootloader configuration is a separate write operation. If the bootloader entry is incorrectly configured, the system may become unbootable even if the build succeeded. This is the primary failure point of the atomic model.
The Mutable State Gap
Rollbacks only revert the system configuration (binaries and system configs). They do not revert mutable data. For example, if a service update migrates a database schema in /var/lib/mysql, rolling back the NixOS generation will revert the MySQL binary, but it will not revert the data migration. This can lead to a version mismatch between the binary and the data.
Design Constraints and Evolution
The atomic store-switch model is ideal for general-purpose workstations and servers, but certain conditions would necessitate a different design:
- Real-Time Requirements: If a system requires sub-millisecond latency updates without the overhead of symlink redirection or service restarts, an imperative in-place update may be required.
- Strict State Preservation: For workloads where the application state must be tightly coupled with the binary version, externalizing state to versioned mounts or using containerized state snapshots is necessary.
- Policy Restrictions: Environments that prohibit sandboxed builds (due to legacy proprietary toolchains that require global environment access) cannot utilize the full reproducibility of this design.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.