Persistence

Persistence Best Practices

Rules for using environment.persistence with impermanence without breaking NixOS activation.

The Rule

Never persist a directory that contains files created by NixOS activation (environment.etc, home.file, home.configFile, home.dataFile). Persist individual files instead.

Why

Impermanence bind-mounts /persist/<path> over /<path> via systemd mount units that run at local-fs.targetbefore NixOS activation. On first boot /persist/<path> is empty, hiding whatever activation places there.

Correct (file-level)

environment.persistence."/persist".files = [
  "/etc/ssh/ssh_host_ed25519_key"
  "/etc/ssh/ssh_host_ed25519_key.pub"
];

Wrong (directory-level)

# /etc/ssh/sshd_config is a symlink created by services.openssh
# via environment.etc. The empty bind mount hides it on first boot.
environment.persistence."/persist".directories = ["/etc/ssh"];

When directory persistence is fine

Directory persistence is safe for runtime data — files created by services at runtime, not by NixOS activation:

  • /var/lib — service databases, state
  • /var/log — log files
  • /var/spool — mail, print queues
  • User caches: .cache, .local/share, .local/state

These directories contain data that accumulates after boot and is never managed declaratively by NixOS.

neededForBoot and hideMounts

  • neededForBoot = true (set by persist.nix): Ensures /persist is mounted in initrd stage 1, before the real root switches. Required for persisted files needed by early boot (SSH host keys, machine-id).

  • hideMounts = true (set in each persist block): Tells GVFS to hide the mount point in file managers. Only affects desktop environments, has no effect on system behavior.

Real-world bug: SSH host keys

The modules/services/net/ssh.nix module persisted /etc/ssh as a directory. On first boot, the bind mount hid the sshd_config symlink that services.openssh creates via environment.etc. sshd failed with “sshd_config: No such file or directory”.

Fix: Persist only the host key files individually. sshd_config is a nix store symlink that doesn’t need persistence.

Real-world bug: Podman registries.conf

The modules/services/virt/podman.nix module persisted /etc/containers as a directory, hiding registries.conf created by environment.etc. Same pattern, same fix: remove the directory persist since the file is NixOS-managed.