{ config, lib, namespace, ... }: with lib; let cfg = config.${namespace}.impermanence; in { imports = [ ./options.nix ]; config = mkIf cfg.enable { security.sudo.extraConfig = '' # rollback results in sudo lectures after each reboot Defaults lecture = never ''; system.activationScripts = { "var-lib-private-permissions" = { deps = [ "createPersistentStorageDirs" ]; text = '' mkdir -p /var/lib/private chmod 0700 /var/lib/private ''; }; }; boot.initrd.systemd.services.rootfs-cleanup = { description = "Clean file system root"; wantedBy = [ "initrd.target" ]; after = [ "initrd-root-device.target" ]; before = [ "sysroot.mount" ]; unitConfig.DefaultDependencies = "no"; serviceConfig.Type = "oneshot"; script = if (hasAttr "/" config.fileSystems) && (config.fileSystems."/".fsType == "btrfs") then '' # workaround for machines without working rtc battery # The time may not yet be correctly set, so wait until it is if [[ $(date '+%s') -lt 1730469314 ]]; then sleep 30 # this should hopefully be enough fi mkdir /btrfs_tmp mount ${config.fileSystems."/".device} -t btrfs /btrfs_tmp if [[ -e /btrfs_tmp/root ]]; then mkdir -p /btrfs_tmp/old_roots timestamp=$(date --date="@$(stat -c %X /btrfs_tmp/root)" "+%Y-%m-%d_%H:%M:%S") mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp" fi delete_subvolume_recursively() { IFS=$'\n' for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do delete_subvolume_recursively "/btrfs_tmp/$i" done btrfs subvolume delete "$1" || rm -rf "$1" } for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -atime +30); do delete_subvolume_recursively "$i" done btrfs subvolume create /btrfs_tmp/root umount /btrfs_tmp '' else if (hasAttr "/" config.fileSystems) && (config.fileSystems."/".fsType == "bcachefs") then '' # workaround for machines without working rtc battery # The time may not yet be correctly set, so wait until it is if [[ $(date '+%s') -lt 1730469314 ]]; then sleep 30 # this should hopefully be enough fi if [[ -e /root_tmp/root ]]; then mkdir -p /root_tmp/old_roots timestamp=$(date --date="@$(stat -c %X /root_tmp/root)" "+%Y-%m-%d_%H:%M:%S") mv /root_tmp/root "/root_tmp/old_roots/$timestamp" fi for i in $(find /root_tmp/old_roots/ -maxdepth 1 -atime +30); do bcachefs subvolume delete $i done bcachefs subvolume create /root_tmp/root '' else # For tmpfs or other filesystems, do nothing ""; }; assertions = [ { assertion = hasAttr "/" config.fileSystems; message = "To use impermanence, you need to define a root volume"; } { assertion = if hasAttr "/" config.fileSystems then config.fileSystems."/".fsType == "btrfs" || config.fileSystems."/".fsType == "bcachefs" || config.fileSystems."/".fsType == "tmpfs" else false; message = "rootfs must be btrfs, bcachefs, or tmpfs; not " + config.fileSystems."/".fsType; } { assertion = if hasAttr "/" config.fileSystems && (config.fileSystems."/".fsType == "btrfs" || config.fileSystems."/".fsType == "bcachefs") then any ( t: t == "subvol=root" || t == "subvol=/root" || t == "X-mount.subdir=subvolumes/root" ) config.fileSystems."/".options else true; message = "btrfs or bcachefs rootfs must mount subvolume root"; } { assertion = !config.boot.isContainer; message = "impermanence is not supported in containers"; } ]; environment.persistence.${cfg.persistencePath} = { hideMounts = true; directories = [ "/var/lib/bluetooth" "/var/lib/iwd" "/var/lib/nixos" "/var/lib/libvirt" "/var/lib/waydroid" "/var/lib/systemd/coredump" "/etc/NetworkManager/system-connections" "/var/lib/tailscale" "/var/lib/homeassistant" "/var/lib/mosquitto" "/var/lib/music-assistant" "/var/lib/postgresql" "/var/lib/zigbee2mqtt" { directory = "/var/lib/colord"; user = "colord"; group = "colord"; mode = "u=rwx,g=rx,o="; } { directory = "/etc/nix"; user = "root"; group = "root"; mode = "u=rwx,g=rx,o=rx"; } { directory = "/var/lib/private/authentik/media"; user = "authentik"; group = "authentik"; mode = "u=rwx,g=,o="; } { directory = "/var/lib/private"; mode = "u=rwx,g=rx,o="; } { directory = "/media/nas"; user = "nas-apps"; group = "jallen-nas"; mode = "u=rwx,g=rx,o=rx"; } { directory = "/var/lib/crowdsec"; user = "crowdsec"; group = "crowdsec"; mode = "u=rwx,g=rwx,o=rx"; } { directory = "/plugins-storage"; user = "traefik"; group = "traefik"; mode = "u=rwx,g=rwx,o=rx"; } ]; files = [ "/etc/machine-id" ]; }; }; }