From 78dd22fed3939862ea1e2714177060a6d143ce5a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 3 Nov 2025 11:55:10 -0600 Subject: [PATCH 01/44] feat: started to draft out new storage interface --- modules/nixos-modules/impermanence.nix | 155 +++++++++++++++++++++---- 1 file changed, 131 insertions(+), 24 deletions(-) diff --git a/modules/nixos-modules/impermanence.nix b/modules/nixos-modules/impermanence.nix index 60011cb..5b1bbd2 100644 --- a/modules/nixos-modules/impermanence.nix +++ b/modules/nixos-modules/impermanence.nix @@ -3,38 +3,145 @@ lib, ... }: { - # options.storage = { - # zfs = { - # # TODO: enable option - # # when this option is enabled we need to configure and enable disko things + options.storage = let + datasetSubmodule = {name, ...}: { + # TODO: we need to figure out what options a dataset can have in zfs + }; - # # TODO: we need some way of managing notifications + impermanenceDatasetSubmodules = [ + datasetSubmodule + ({...}: let + pathPermissions = { + read = lib.mkEnableOption "should the path have read permissions"; + write = lib.mkEnableOption "should the path have read permissions"; + execute = lib.mkEnableOption "should the path have read permissions"; + }; + pathTypeSubmodule = {name, ...}: { + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + owner = { + user = lib.mkOption { + type = lib.types.str; + default = "nouser"; + }; + permissions = pathPermissions; + }; + group = { + group = lib.mkOption { + type = lib.types.str; + default = "nogroup"; + }; + permissions = pathPermissions; + }; + other = { + permissions = pathPermissions; + }; + }; + }; + in { + options = { + files = lib.types.mkOption { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + directories = { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + }; + }) + ]; + in { + zfs = { + # TODO: enable option implementation + enable = lib.mkEnableOption "Should zfs be enabled on this system."; - # # TODO: we need options to configure zfs pools - # # we should have warnings when the configured pool is missing drives + notifications = { + enable = lib.mkEnableOption "are notifications enabled"; + host = lib.mkOption { + type = lib.types.str; + description = "what is the host that we are going to send the email to"; + }; + port = lib.mkOption { + type = lib.types.port; + description = "what port is the host using to receive mail on"; + }; + to = lib.mkOption { + type = lib.types.str; + description = "what account is the email going to be sent to"; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user is the email going to be set from"; + }; + tokenFile = lib.mkOption { + type = lib.types.str; + description = "file containing the password to be used by msmtp for notifications"; + }; + }; - # # TODO: dataset option that is a submodule that adds datasets to the system - # # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it + # TODO: we need options to configure zfs pools + # we should have warnings when the configured pool is missing drives after activation + # TODO: implementation of this + # TODO: validations that we have at least one boot drive + pool = let + deviceType = + lib.types.coercedTo lib.types.str (device: { + device = device; + boot = false; + }) { + device = lib.mkOption { + type = lib.types.str; + }; + boot = lib.mkEnableOption "should this device be a boot device"; + }; + in { + encryption = lib.mkEnableOption "Should encryption be enabled on this pool."; + vdevs = lib.mkOption { + type = lib.types.listOf deviceType; + default = []; + }; + cache = lib.mkOption { + type = lib.types.attrsOf deviceType; + }; + }; - # # TODO: pools and datasets need to be passed to disko - # }; + # TODO:create the root dataset automatically + # TODO: dataset option that is a submodule that adds datasets to the system + # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it + datasets = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + }; - # impermanence = { - # # TODO: enable option + impermanence = { + enable = lib.mkEnableOption "should impermanence be enabled for this system"; + # TODO: enable option implementation - # # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system - # # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets - # # Then we should make a dataset for user folders local and persist - # # We should also create datasets for systemd modules that have have impermanence enabled for them - # # we need to figure out what options a dataset can have in zfs - # }; + # TODO: assertion that zfs needs to be enabled when impermanence is enabled - # # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface + # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system + # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets + # We should also create datasets for systemd modules that have have impermanence enabled for them - # # TODO: we should have a way of enabling impermanence for a systemd config - # # these should have an option to put their folder into their own dataset (this needs to support private vs non private) - # # options for features that can be added to the dataset - # }; + datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + + # TODO: this should just live under home-manager.users..storage.impermanence + home-manager = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + enable = lib.mkEnableOption "should impermanence be enabled for this user"; + + # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets + datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + })); + }; + + # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface + + # TODO: we should have a way of enabling impermanence for a systemd config + # these should have an option to put their folder into their own dataset (this needs to support private vs non private) + # options for features that can be added to the dataset + }; options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device"; From 573708fd479ea551a83c54d120f22b9ca5ae6496 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Tue, 4 Nov 2025 15:02:49 -0600 Subject: [PATCH 02/44] moved storage option draft to its own folder --- modules/nixos-modules/default.nix | 1 + modules/nixos-modules/impermanence.nix | 140 ------------------ modules/nixos-modules/storage/default.nix | 12 ++ .../nixos-modules/storage/impermanence.nix | 31 ++++ .../storage/submodules/dataset.nix | 3 + .../submodules/impermanenceDataset.nix | 46 ++++++ modules/nixos-modules/storage/zfs.nix | 67 +++++++++ 7 files changed, 160 insertions(+), 140 deletions(-) create mode 100644 modules/nixos-modules/storage/default.nix create mode 100644 modules/nixos-modules/storage/impermanence.nix create mode 100644 modules/nixos-modules/storage/submodules/dataset.nix create mode 100644 modules/nixos-modules/storage/submodules/impermanenceDataset.nix create mode 100644 modules/nixos-modules/storage/zfs.nix diff --git a/modules/nixos-modules/default.nix b/modules/nixos-modules/default.nix index 2ba1a58..77bfe93 100644 --- a/modules/nixos-modules/default.nix +++ b/modules/nixos-modules/default.nix @@ -16,6 +16,7 @@ ./tailscale.nix ./steam.nix ./server + ./storage ]; nixpkgs.config.permittedInsecurePackages = [ diff --git a/modules/nixos-modules/impermanence.nix b/modules/nixos-modules/impermanence.nix index 5b1bbd2..4cdcd00 100644 --- a/modules/nixos-modules/impermanence.nix +++ b/modules/nixos-modules/impermanence.nix @@ -3,146 +3,6 @@ lib, ... }: { - options.storage = let - datasetSubmodule = {name, ...}: { - # TODO: we need to figure out what options a dataset can have in zfs - }; - - impermanenceDatasetSubmodules = [ - datasetSubmodule - ({...}: let - pathPermissions = { - read = lib.mkEnableOption "should the path have read permissions"; - write = lib.mkEnableOption "should the path have read permissions"; - execute = lib.mkEnableOption "should the path have read permissions"; - }; - pathTypeSubmodule = {name, ...}: { - options = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - owner = { - user = lib.mkOption { - type = lib.types.str; - default = "nouser"; - }; - permissions = pathPermissions; - }; - group = { - group = lib.mkOption { - type = lib.types.str; - default = "nogroup"; - }; - permissions = pathPermissions; - }; - other = { - permissions = pathPermissions; - }; - }; - }; - in { - options = { - files = lib.types.mkOption { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - directories = { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - }; - }) - ]; - in { - zfs = { - # TODO: enable option implementation - enable = lib.mkEnableOption "Should zfs be enabled on this system."; - - notifications = { - enable = lib.mkEnableOption "are notifications enabled"; - host = lib.mkOption { - type = lib.types.str; - description = "what is the host that we are going to send the email to"; - }; - port = lib.mkOption { - type = lib.types.port; - description = "what port is the host using to receive mail on"; - }; - to = lib.mkOption { - type = lib.types.str; - description = "what account is the email going to be sent to"; - }; - user = lib.mkOption { - type = lib.types.str; - description = "what user is the email going to be set from"; - }; - tokenFile = lib.mkOption { - type = lib.types.str; - description = "file containing the password to be used by msmtp for notifications"; - }; - }; - - # TODO: we need options to configure zfs pools - # we should have warnings when the configured pool is missing drives after activation - # TODO: implementation of this - # TODO: validations that we have at least one boot drive - pool = let - deviceType = - lib.types.coercedTo lib.types.str (device: { - device = device; - boot = false; - }) { - device = lib.mkOption { - type = lib.types.str; - }; - boot = lib.mkEnableOption "should this device be a boot device"; - }; - in { - encryption = lib.mkEnableOption "Should encryption be enabled on this pool."; - vdevs = lib.mkOption { - type = lib.types.listOf deviceType; - default = []; - }; - cache = lib.mkOption { - type = lib.types.attrsOf deviceType; - }; - }; - - # TODO:create the root dataset automatically - # TODO: dataset option that is a submodule that adds datasets to the system - # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it - datasets = lib.types.attrsOf (lib.types.submodule datasetSubmodule); - }; - - impermanence = { - enable = lib.mkEnableOption "should impermanence be enabled for this system"; - # TODO: enable option implementation - - # TODO: assertion that zfs needs to be enabled when impermanence is enabled - - # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system - # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets - # We should also create datasets for systemd modules that have have impermanence enabled for them - - datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); - - # TODO: this should just live under home-manager.users..storage.impermanence - home-manager = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - enable = lib.mkEnableOption "should impermanence be enabled for this user"; - - # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets - datasets = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); - })); - }; - - # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface - - # TODO: we should have a way of enabling impermanence for a systemd config - # these should have an option to put their folder into their own dataset (this needs to support private vs non private) - # options for features that can be added to the dataset - }; - options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device"; config = lib.mkMerge [ diff --git a/modules/nixos-modules/storage/default.nix b/modules/nixos-modules/storage/default.nix new file mode 100644 index 0000000..02f7fb9 --- /dev/null +++ b/modules/nixos-modules/storage/default.nix @@ -0,0 +1,12 @@ +{...}: { + # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface + + # TODO: we should have a way of enabling impermanence for a systemd config + # these should have an option to put their folder into their own dataset (this needs to support private vs non private) + # options for features that can be added to the dataset + + imports = [ + ./impermanence.nix + ./zfs.nix + ]; +} diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix new file mode 100644 index 0000000..c51e35f --- /dev/null +++ b/modules/nixos-modules/storage/impermanence.nix @@ -0,0 +1,31 @@ +args @ {lib, ...}: let + impermanenceDatasetSubmodules = (import ./submodules/impermanenceDataset.nix) args; +in { + options.storage = { + impermanence = { + enable = lib.mkEnableOption "should impermanence be enabled for this system"; + # TODO: enable option implementation + + # TODO: assertion that zfs needs to be enabled when impermanence is enabled + + # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system + # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets + # We should also create datasets for systemd modules that have have impermanence enabled for them + + datasets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + }; + + # TODO: this should just live under home-manager.users..storage.impermanence + home-manager = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + enable = lib.mkEnableOption "should impermanence be enabled for this user"; + # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets + datasets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + }; + })); + }; + }; + }; +} diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix new file mode 100644 index 0000000..a6cc3e6 --- /dev/null +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -0,0 +1,3 @@ +{name, ...}: { + # TODO: we need to figure out what options a dataset can have in zfs +} diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix new file mode 100644 index 0000000..f9a4df6 --- /dev/null +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -0,0 +1,46 @@ +{lib, ...}: let + pathPermissions = { + read = lib.mkEnableOption "should the path have read permissions"; + write = lib.mkEnableOption "should the path have read permissions"; + execute = lib.mkEnableOption "should the path have read permissions"; + }; + pathTypeSubmodule = {name, ...}: { + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + owner = { + user = lib.mkOption { + type = lib.types.str; + default = "nouser"; + }; + permissions = pathPermissions; + }; + group = { + group = lib.mkOption { + type = lib.types.str; + default = "nogroup"; + }; + permissions = pathPermissions; + }; + other = { + permissions = pathPermissions; + }; + }; + }; +in { + imports = [ + ./dataset.nix + ]; + options = { + files = lib.types.mkOption { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + directories = { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + }; +} diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix new file mode 100644 index 0000000..c5dd412 --- /dev/null +++ b/modules/nixos-modules/storage/zfs.nix @@ -0,0 +1,67 @@ +args @ {lib, ...}: let + datasetSubmodule = (import ./submodules/dataset.nix) args; +in { + options.storage = { + zfs = { + # TODO: enable option implementation + enable = lib.mkEnableOption "Should zfs be enabled on this system."; + + notifications = { + enable = lib.mkEnableOption "are notifications enabled"; + host = lib.mkOption { + type = lib.types.str; + description = "what is the host that we are going to send the email to"; + }; + port = lib.mkOption { + type = lib.types.port; + description = "what port is the host using to receive mail on"; + }; + to = lib.mkOption { + type = lib.types.str; + description = "what account is the email going to be sent to"; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user is the email going to be set from"; + }; + tokenFile = lib.mkOption { + type = lib.types.str; + description = "file containing the password to be used by msmtp for notifications"; + }; + }; + + # TODO: we need options to configure zfs pools + # we should have warnings when the configured pool is missing drives after activation + # TODO: implementation of this + # TODO: validations that we have at least one boot drive + pool = let + deviceType = + lib.types.coercedTo lib.types.str (device: { + device = device; + boot = false; + }) { + device = lib.mkOption { + type = lib.types.str; + }; + boot = lib.mkEnableOption "should this device be a boot device"; + }; + in { + encryption = lib.mkEnableOption "Should encryption be enabled on this pool."; + vdevs = lib.mkOption { + type = lib.types.listOf deviceType; + default = []; + }; + cache = lib.mkOption { + type = lib.types.attrsOf deviceType; + }; + }; + + # TODO:create the root dataset automatically + # TODO: dataset option that is a submodule that adds datasets to the system + # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it + datasets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + }; + }; + }; +} From 2fd14e4cc0c4c8f8a05ed91a67e9d868c67dd783 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Tue, 4 Nov 2025 19:39:27 -0600 Subject: [PATCH 03/44] feat: added config block to zfs.nix and gave it notification functionality --- modules/nixos-modules/storage/zfs.nix | 70 ++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index c5dd412..e5793ca 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -1,9 +1,13 @@ -args @ {lib, ...}: let +args @ { + lib, + pkgs, + config, + ... +}: let datasetSubmodule = (import ./submodules/dataset.nix) args; in { options.storage = { zfs = { - # TODO: enable option implementation enable = lib.mkEnableOption "Should zfs be enabled on this system."; notifications = { @@ -30,10 +34,6 @@ in { }; }; - # TODO: we need options to configure zfs pools - # we should have warnings when the configured pool is missing drives after activation - # TODO: implementation of this - # TODO: validations that we have at least one boot drive pool = let deviceType = lib.types.coercedTo lib.types.str (device: { @@ -56,7 +56,7 @@ in { }; }; - # TODO:create the root dataset automatically + # TODO: create the root dataset automatically # TODO: dataset option that is a submodule that adds datasets to the system # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it datasets = lib.mkOption { @@ -64,4 +64,60 @@ in { }; }; }; + + config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + services.zfs = { + autoScrub.enable = true; + autoSnapshot.enable = true; + }; + + # TODO: post activation script that makes sure that our configured pool match the pool that exist on the system + # TODO: validation that we have a boot drive + # TODO: disko config mapping + } + (lib.mkIf config.storage.zfs.notifications.enable { + programs.msmtp = { + enable = true; + setSendmail = true; + defaults = { + aliases = "/etc/aliases"; + port = config.storage.zfs.notifications.port; + tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; + tls = "on"; + auth = "login"; + tls_starttls = "off"; + }; + accounts = { + zfs_notifications = { + auth = true; + tls = true; + host = config.storage.zfs.notifications.host; + passwordeval = "cat ${config.storage.zfs.notifications.tokenFile}"; + user = config.storage.zfs.notifications.user; + from = config.storage.zfs.notifications.user; + }; + }; + }; + + services.zfs = { + zed = { + enableMail = true; + + settings = { + ZED_DEBUG_LOG = "/tmp/zed.debug.log"; + ZED_EMAIL_ADDR = [config.storage.zfs.notifications.to]; + ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; + ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; + + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = true; + + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = true; + }; + }; + }; + }) + ]); } From d8989bb43d3353aaf2e11b01d3ea797502612436 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Wed, 5 Nov 2025 10:56:04 -0600 Subject: [PATCH 04/44] feat: drafted out zfs vdev, pool, and dataset implementations --- .../nixos-modules/storage/impermanence.nix | 2 + .../storage/submodules/dataset.nix | 97 +++- .../submodules/impermanenceDataset.nix | 2 +- modules/nixos-modules/storage/zfs.nix | 460 +++++++++++++++++- 4 files changed, 551 insertions(+), 10 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index c51e35f..b1fd6b5 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -14,6 +14,7 @@ in { datasets = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + default = {}; }; # TODO: this should just live under home-manager.users..storage.impermanence @@ -23,6 +24,7 @@ in { # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets datasets = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + default = {}; }; })); }; diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index a6cc3e6..482671e 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -1,3 +1,96 @@ -{name, ...}: { - # TODO: we need to figure out what options a dataset can have in zfs +{lib, ...}: {name, ...}: { + options = { + type = lib.mkOption { + type = lib.types.enum ["zfs_fs" "zfs_volume"]; + default = "zfs_fs"; + description = "Type of ZFS dataset (filesystem or volume)"; + }; + + # ZFS dataset options that match what's currently hardcoded in rootFsOptions + canmount = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "noauto"]); + default = null; + description = "Controls whether the file system can be mounted"; + }; + + mountpoint = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Controls the mount point used for this file system"; + }; + + xattr = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); + default = null; + description = "Extended attribute storage method"; + }; + + acltype = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["off" "nfsv4" "posixacl"]); + default = null; + description = "Access control list type"; + }; + + relatime = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off"]); + default = null; + description = "Controls when access time is updated"; + }; + + compression = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "lz4" "gzip" "zstd" "lzjb" "zle"]); + default = null; + description = "Compression algorithm to use"; + }; + + encryption = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]); + default = null; + description = "Encryption algorithm to use"; + }; + + keyformat = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["raw" "hex" "passphrase"]); + default = null; + description = "Format of the encryption key"; + }; + + keylocation = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Location of the encryption key"; + }; + + autoSnapshot = lib.mkOption { + type = lib.types.nullOr lib.types.bool; + default = null; + description = "Enable automatic snapshots for this dataset"; + }; + + # Additional common ZFS options + recordsize = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Suggested block size for files in the file system"; + }; + + sync = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["standard" "always" "disabled"]); + default = null; + description = "Synchronous write behavior"; + }; + + atime = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off"]); + default = null; + description = "Controls whether access time is updated"; + }; + + # Custom options for disko integration + postCreateHook = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Script to run after dataset creation"; + }; + }; } diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index f9a4df6..193ab80 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -1,4 +1,4 @@ -{lib, ...}: let +{lib, ...}: {...}: let pathPermissions = { read = lib.mkEnableOption "should the path have read permissions"; write = lib.mkEnableOption "should the path have read permissions"; diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index e5793ca..20e41ae 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -5,6 +5,49 @@ args @ { ... }: let datasetSubmodule = (import ./submodules/dataset.nix) args; + + # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that + hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + + poolVdevs = [ + (builtins.map ( + device: let + deviceStr = + if builtins.isString device + then device + else device.device; + in + lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr + ) + config.storage.zfs.pool.vdevs) + ]; + + poolCache = builtins.map ( + name: let + device = config.storage.zfs.pool.cache.${name}; + deviceStr = + if builtins.isString device + then device + else device.device; + in + lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr + ) (builtins.attrNames config.storage.zfs.pool.cache); + + bootDrives = + builtins.map ( + device: + if builtins.isString device + then device + else device.device + ) (builtins.filter ( + device: + if builtins.isString device + then false + else device.boot + ) + config.storage.zfs.pool.vdevs); + + allDrives = (lib.lists.flatten poolVdevs) ++ poolCache; in { options.storage = { zfs = { @@ -46,35 +89,438 @@ in { boot = lib.mkEnableOption "should this device be a boot device"; }; in { - encryption = lib.mkEnableOption "Should encryption be enabled on this pool."; + encryption = { + enable = lib.mkEnableOption "Should encryption be enabled on this pool."; + keyformat = lib.mkOption { + type = lib.types.enum ["raw" "hex" "passphrase"]; + default = "hex"; + description = "Format of the encryption key"; + }; + keylocation = lib.mkOption { + type = lib.types.str; + default = "prompt"; + description = "Location of the encryption key"; + }; + }; + mode = lib.mkOption { + type = lib.types.enum ["stripe" "mirror" "raidz1" "raidz2" "raidz3"]; + default = "raidz2"; + description = "ZFS redundancy mode for the pool"; + }; + bootPartitionSize = lib.mkOption { + type = lib.types.str; + default = "2G"; + description = "Size of the boot partition on boot drives"; + }; vdevs = lib.mkOption { type = lib.types.listOf deviceType; default = []; }; cache = lib.mkOption { type = lib.types.attrsOf deviceType; + default = {}; }; }; - # TODO: create the root dataset automatically - # TODO: dataset option that is a submodule that adds datasets to the system - # warnings for when a dataset was created in the past on a system but it is now missing some of the options defined for it + rootDataset = lib.mkOption { + type = lib.types.submodule datasetSubmodule; + description = "Root ZFS dataset to create"; + default = {}; + }; + datasets = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + description = "Additional ZFS datasets to create"; + default = {}; }; }; }; config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { + assertions = [ + { + assertion = builtins.length bootDrives > 0; + message = '' + ZFS configuration requires at least one boot drive. Please configure at least one device with boot = true in storage.zfs.pool.vdevs. + ''; + } + { + assertion = + !( + config.storage.zfs.pool.encryption.enable + && (config.storage.zfs.rootDataset.encryption + != null + || config.storage.zfs.rootDataset.keyformat != null + || config.storage.zfs.rootDataset.keylocation != null) + ); + message = '' + Cannot set encryption options in both pool.encryption and rootDataset. + Use either pool.encryption for default settings or rootDataset encryption options for explicit control, but not both. + ''; + } + ]; + services.zfs = { autoScrub.enable = true; autoSnapshot.enable = true; }; - # TODO: post activation script that makes sure that our configured pool match the pool that exist on the system - # TODO: validation that we have a boot drive - # TODO: disko config mapping + # Disko configuration based on pool settings + disko.devices = { + disk = ( + builtins.listToAttrs ( + builtins.map + (drive: + lib.attrsets.nameValuePair (drive.name) { + type = "disk"; + device = "/dev/disk/by-id/${drive.value}"; + content = { + type = "gpt"; + partitions = { + ESP = lib.mkIf (builtins.elem drive.value bootDrives) { + size = config.storage.zfs.pool.bootPartitionSize; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "rpool"; + }; + }; + }; + }; + }) + allDrives + ) + ); + zpool = { + rpool = { + type = "zpool"; + mode = { + topology = { + type = "topology"; + vdev = ( + builtins.map (disks: { + mode = config.storage.zfs.pool.mode; + members = + builtins.map (disk: disk.name) disks; + }) + poolVdevs + ); + cache = builtins.map (disk: disk.name) poolCache; + }; + }; + + options = { + ashift = "12"; + autotrim = "on"; + }; + + rootFsOptions = let + rootDataset = config.storage.zfs.rootDataset; + # Start with defaults that match the original hardcoded values + defaults = { + canmount = "off"; + mountpoint = "none"; + xattr = "sa"; + acltype = "posixacl"; + relatime = "on"; + compression = "lz4"; + "com.sun:auto-snapshot" = "false"; + }; + # Override defaults with non-null values from rootDataset + userOptions = lib.attrsets.filterAttrs (_: v: v != null) { + canmount = rootDataset.canmount; + mountpoint = rootDataset.mountpoint; + xattr = rootDataset.xattr; + acltype = rootDataset.acltype; + relatime = rootDataset.relatime; + compression = rootDataset.compression; + encryption = rootDataset.encryption; + keyformat = rootDataset.keyformat; + keylocation = rootDataset.keylocation; + recordsize = rootDataset.recordsize; + sync = rootDataset.sync; + atime = rootDataset.atime; + "com.sun:auto-snapshot" = + if rootDataset.autoSnapshot == null + then null + else + ( + if rootDataset.autoSnapshot + then "true" + else "false" + ); + }; + # Only apply pool encryption if user hasn't set encryption options in rootDataset + poolEncryptionOptions = + lib.attrsets.optionalAttrs ( + config.storage.zfs.pool.encryption.enable + && rootDataset.encryption == null + && rootDataset.keyformat == null + && rootDataset.keylocation == null + ) { + encryption = "on"; + keyformat = config.storage.zfs.pool.encryption.keyformat; + keylocation = config.storage.zfs.pool.encryption.keylocation; + }; + in + defaults // userOptions // rootDataset.options // poolEncryptionOptions; + + datasets = lib.mkMerge [ + ( + lib.attrsets.mapAttrs (name: value: { + type = value.type; + options = let + # For datasets, only include non-null user-specified values + userOptions = lib.attrsets.filterAttrs (_: v: v != null) { + canmount = value.canmount; + xattr = value.xattr; + acltype = value.acltype; + relatime = value.relatime; + compression = value.compression; + encryption = value.encryption; + keyformat = value.keyformat; + keylocation = value.keylocation; + recordsize = value.recordsize; + sync = value.sync; + atime = value.atime; + "com.sun:auto-snapshot" = + if value.autoSnapshot == null + then null + else + ( + if value.autoSnapshot + then "true" + else "false" + ); + }; + in + userOptions // (value.options or {}); + mountpoint = value.mountpoint; + postCreateHook = value.postCreateHook or ""; + }) + config.storage.zfs.datasets + ) + ]; + }; + }; + }; + + # Post-activation scripts for validation + system.activationScripts = { + # Script 1: Validate pool, cache devices, and vdevs + zfs-pool-validation = { + text = '' + echo "Running ZFS pool validation..." + + # Function to check if a device exists in a vdev or cache + check_device_in_pool() { + local device_id="$1" + local device_type="$2" # "cache" or "vdev" + + if ! zpool status rpool | grep -q "$device_id"; then + echo "ERROR: Device $device_id not found in pool rpool ($device_type)" + return 1 + fi + return 0 + } + + # Function to validate vdev configuration + validate_vdevs() { + local expected_mode="${config.storage.zfs.pool.mode}" + local pool_status=$(zpool status rpool) + + # Check if pool exists + if ! zpool list rpool >/dev/null 2>&1; then + echo "ERROR: ZFS pool 'rpool' does not exist" + return 1 + fi + + # Validate each configured vdev device + ${lib.concatMapStringsSep "\n" ( + device: let + deviceStr = + if builtins.isString device + then device + else device.device; + in '' + if ! check_device_in_pool "${deviceStr}" "vdev"; then + echo "ERROR: Vdev device ${deviceStr} not found in pool" + exit 1 + fi + '' + ) + config.storage.zfs.pool.vdevs} + + # Check pool mode matches configuration + if ! echo "$pool_status" | grep -q "$expected_mode"; then + echo "WARNING: Pool mode may not match expected configuration ($expected_mode)" + fi + + echo "✓ All vdev devices validated successfully" + return 0 + } + + # Function to validate cache configuration + validate_cache() { + ${lib.concatMapStringsSep "\n" ( + name: let + device = config.storage.zfs.pool.cache.${name}; + deviceStr = + if builtins.isString device + then device + else device.device; + in '' + if ! check_device_in_pool "${deviceStr}" "cache"; then + echo "ERROR: Cache device ${deviceStr} (${name}) not found in pool" + exit 1 + fi + '' + ) (builtins.attrNames config.storage.zfs.pool.cache)} + + echo "✓ All cache devices validated successfully" + return 0 + } + + # Run validations + if validate_vdevs && validate_cache; then + echo "✓ ZFS pool validation completed successfully" + else + echo "✗ ZFS pool validation failed" + exit 1 + fi + ''; + deps = ["zfs"]; + }; + + # Script 2: Validate datasets and their options + zfs-dataset-validation = { + text = '' + echo "Running ZFS dataset validation..." + + # Function to check if dataset exists + check_dataset_exists() { + local dataset="$1" + if ! zfs list "$dataset" >/dev/null 2>&1; then + echo "ERROR: Dataset $dataset does not exist" + return 1 + fi + return 0 + } + + # Function to validate dataset options + validate_dataset_options() { + local dataset="$1" + local expected_options="$2" + + # Parse expected options (format: "option=value option2=value2") + echo "$expected_options" | tr ' ' '\n' | while IFS='=' read -r option expected_value; do + if [ -n "$option" ] && [ -n "$expected_value" ]; then + local actual_value=$(zfs get -H -o value "$option" "$dataset" 2>/dev/null) + if [ "$actual_value" != "$expected_value" ]; then + echo "ERROR: Dataset $dataset option $option is '$actual_value', expected '$expected_value'" + return 1 + fi + fi + done + return 0 + } + + # Validate root dataset + echo "Validating root dataset..." + if check_dataset_exists "rpool"; then + root_options="" + ${lib.concatMapStringsSep "\n" ( + option: let + value = config.storage.zfs.rootDataset.${option}; + in + lib.optionalString (value != null) '' + root_options="$root_options ${option}=${toString value}" + '' + ) ["canmount" "xattr" "acltype" "relatime" "compression" "encryption" "keyformat" "keylocation" "recordsize" "sync" "atime"]} + + # Add autoSnapshot option + ${lib.optionalString (config.storage.zfs.rootDataset.autoSnapshot != null) '' + root_options="$root_options com.sun:auto-snapshot=${ + if config.storage.zfs.rootDataset.autoSnapshot + then "true" + else "false" + }" + ''} + + if validate_dataset_options "rpool" "$root_options"; then + echo "✓ Root dataset options validated" + else + echo "✗ Root dataset validation failed" + exit 1 + fi + else + echo "✗ Root dataset validation failed" + exit 1 + fi + + # Validate configured datasets + ${lib.concatMapStringsSep "\n" ( + name: let + dataset = config.storage.zfs.datasets.${name}; + in '' + echo "Validating dataset: rpool/${name}" + if check_dataset_exists "rpool/${name}"; then + dataset_options="" + ${lib.concatMapStringsSep "\n" ( + option: let + value = dataset.${option}; + in + lib.optionalString (value != null) '' + dataset_options="$dataset_options ${option}=${toString value}" + '' + ) ["canmount" "xattr" "acltype" "relatime" "compression" "encryption" "keyformat" "keylocation" "recordsize" "sync" "atime"]} + + # Add autoSnapshot option + ${lib.optionalString (dataset.autoSnapshot != null) '' + dataset_options="$dataset_options com.sun:auto-snapshot=${ + if dataset.autoSnapshot + then "true" + else "false" + }" + ''} + + # Add custom options + ${lib.concatMapStringsSep "\n" ( + optName: let + optValue = dataset.options.${optName}; + in '' + dataset_options="$dataset_options ${optName}=${toString optValue}" + '' + ) (builtins.attrNames (dataset.options or {}))} + + if validate_dataset_options "rpool/${name}" "$dataset_options"; then + echo "✓ Dataset rpool/${name} options validated" + else + echo "✗ Dataset rpool/${name} validation failed" + exit 1 + fi + else + echo "✗ Dataset rpool/${name} validation failed" + exit 1 + fi + '' + ) (builtins.attrNames config.storage.zfs.datasets)} + + echo "✓ ZFS dataset validation completed successfully" + ''; + deps = ["zfs" "zfs-pool-validation"]; + }; + }; } (lib.mkIf config.storage.zfs.notifications.enable { programs.msmtp = { From adc6b90c93dd402ec2fb4c77d94bfa4fc8ab5f33 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 7 Nov 2025 16:29:56 -0600 Subject: [PATCH 05/44] feat: made impermanence create datasets for zfs and persistence --- .../nixos-modules/storage/impermanence.nix | 101 ++++++++++++++---- modules/nixos-modules/storage/storage.nix | 73 +++++++++++++ .../submodules/impermanenceDataset.nix | 23 ++-- modules/nixos-modules/storage/zfs.nix | 34 +++--- 4 files changed, 188 insertions(+), 43 deletions(-) create mode 100644 modules/nixos-modules/storage/storage.nix diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index b1fd6b5..c5f53a3 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -1,33 +1,90 @@ -args @ {lib, ...}: let - impermanenceDatasetSubmodules = (import ./submodules/impermanenceDataset.nix) args; +args @ { + lib, + config, + ... +}: let + datasetSubmodules = (import ./submodules/dataset.nix) args; + impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; + + permissionsToMode = permissions: let + permSetToDigit = permSet: + ( + if permSet.read + then 4 + else 0 + ) + + ( + if permSet.write + then 2 + else 0 + ) + + ( + if permSet.execute + then 1 + else 0 + ); + + ownerDigit = permSetToDigit permissions.owner.permissions; + groupDigit = permSetToDigit permissions.group.permissions; + otherDigit = permSetToDigit permissions.other.permissions; + in + toString ownerDigit + toString groupDigit + toString otherDigit; + + # Get the option names from both submodules to automatically determine which are impermanence-specific + regularDatasetEval = lib.evalModules { + modules = [datasetSubmodules]; + specialArgs = args; + }; + impermanenceDatasetEval = lib.evalModules { + modules = [impermanenceDatasetSubmodule]; + specialArgs = args; + }; + + regularDatasetOptions = builtins.attrNames regularDatasetEval.options; + impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; + + # Find options that are only in impermanence datasets (not in regular ZFS datasets) + impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; in { options.storage = { impermanence = { enable = lib.mkEnableOption "should impermanence be enabled for this system"; - # TODO: enable option implementation - - # TODO: assertion that zfs needs to be enabled when impermanence is enabled - - # TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system - # We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets - # We should also create datasets for systemd modules that have have impermanence enabled for them datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); default = {}; }; - - # TODO: this should just live under home-manager.users..storage.impermanence - home-manager = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - enable = lib.mkEnableOption "should impermanence be enabled for this user"; - # We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets - datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules); - default = {}; - }; - })); - }; }; }; + + config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ + { + environment.persistence = + lib.mapAttrs (datasetName: dataset: { + enable = true; + hideMounts = true; + directories = lib.mapAttrsToList (path: dirConfig: { + directory = path; + user = dirConfig.owner.name; + group = dirConfig.group.name; + mode = permissionsToMode dirConfig; + }) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories); + files = lib.mapAttrsToList (path: fileConfig: { + file = path; + user = fileConfig.owner.name; + group = fileConfig.group.name; + mode = permissionsToMode fileConfig; + }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); + }) + config.storage.impermanence.datasets; + } + (lib.mkIf config.storage.zfs.enable { + storage.zfs.datasets = + lib.mapAttrs ( + datasetName: dataset: + builtins.removeAttrs dataset impermanenceOnlyOptions + ) + config.storage.impermanence.datasets; + }) + ]); } diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix new file mode 100644 index 0000000..1c1986a --- /dev/null +++ b/modules/nixos-modules/storage/storage.nix @@ -0,0 +1,73 @@ +{ + lib, + config, + util, + ... +}: { + # TODO: create all of the datasets from option and home-manager datasets + # TODO: set up datasets for systemd services that want a dataset created + config = lib.mkMerge [ + ( + lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + storage.zfs.datasets = { + "persist/system/nix" = { + type = "zfs_fs"; + mountpoint = "/nix"; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + "com.sun:auto-snapshot" = "false"; + }; + }; + "persist/system/var/log" = { + type = "zfs_fs"; + mountpoint = "/persist/system/var/log"; + options = { + "com.sun:auto-snapshot" = "false"; + }; + }; + }; + } + (util.mkUnless config.storage.impermanence.enable { + # TODO: configure datasets for normal zfs + # TODO: create datasets for systemd.services..storage.impermanence.datasets + storage.zfs.datasets = { + "persist/system/root" = { + type = "zfs_fs"; + mountpoint = "/"; + canmount = "on"; + }; + }; + }) + (lib.mkIf config.storage.impermanence.enable { + storage.impermanence.datasets = { + "persist/system/root" = { + type = "zfs_fs"; + }; + }; + storage.zfs.datasets = { + # TODO: is there a way that we can link these two folders in configs via storage.impermanence.datasets + "local/system/root" = { + type = "zfs_fs"; + mountpoint = "/"; + options = { + canmount = "on"; + }; + postCreateHook = '' + zfs snapshot rpool/local/system/root@blank + ''; + }; + }; + + # TODO: home-manager.users..storage.impermanence.enable + # is false then persist the entire directory of the user + # if true persist home-manager.users..storage.impermanence.datasets + # TODO: systemd.services..storage.datasets persists + }) + ]) + ) + # TODO: configure other needed storage modes here + ]; +} diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 193ab80..2169ec1 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -1,4 +1,10 @@ -{lib, ...}: {...}: let +args @ { + lib, + name, + ... +}: {...}: let + datasetSubmodule = (import ./dataset.nix) args; + pathPermissions = { read = lib.mkEnableOption "should the path have read permissions"; write = lib.mkEnableOption "should the path have read permissions"; @@ -11,14 +17,14 @@ default = true; }; owner = { - user = lib.mkOption { + name = lib.mkOption { type = lib.types.str; default = "nouser"; }; permissions = pathPermissions; }; group = { - group = lib.mkOption { + name = lib.mkOption { type = lib.types.str; default = "nogroup"; }; @@ -31,16 +37,21 @@ }; in { imports = [ - ./dataset.nix + datasetSubmodule ]; + options = { - files = lib.types.mkOption { + files = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); default = {}; }; - directories = { + directories = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); default = {}; }; }; + + config = { + mountpoint = "/${name}"; + }; } diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 20e41ae..bf0c609 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -9,18 +9,21 @@ args @ { # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); - poolVdevs = [ - (builtins.map ( - device: let - deviceStr = - if builtins.isString device - then device - else device.device; - in - lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr - ) - config.storage.zfs.pool.vdevs) - ]; + poolVdevs = + builtins.map ( + vdev: + builtins.map ( + device: let + deviceStr = + if builtins.isString device + then device + else device.device; + in + lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr + ) + vdev + ) + config.storage.zfs.pool.vdevs; poolCache = builtins.map ( name: let @@ -45,7 +48,7 @@ args @ { then false else device.boot ) - config.storage.zfs.pool.vdevs); + (lib.lists.flatten config.storage.zfs.pool.vdevs)); allDrives = (lib.lists.flatten poolVdevs) ++ poolCache; in { @@ -113,8 +116,9 @@ in { description = "Size of the boot partition on boot drives"; }; vdevs = lib.mkOption { - type = lib.types.listOf deviceType; + type = lib.types.listOf (lib.types.listOf deviceType); default = []; + description = "List of vdevs, where each vdev is a list of devices"; }; cache = lib.mkOption { type = lib.types.attrsOf deviceType; @@ -359,7 +363,7 @@ in { fi '' ) - config.storage.zfs.pool.vdevs} + (lib.lists.flatten config.storage.zfs.pool.vdevs)} # Check pool mode matches configuration if ! echo "$pool_status" | grep -q "$expected_mode"; then From 0de97fa4a2869bbce5954ee6ab21867b232a231a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 7 Nov 2025 18:14:00 -0600 Subject: [PATCH 06/44] feat: added more development notes --- modules/nixos-modules/storage/impermanence.nix | 11 +++++++++++ modules/nixos-modules/storage/storage.nix | 12 +++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index c5f53a3..470ce48 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -59,6 +59,13 @@ in { config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ { + assertions = [ + { + assertion = config.storage.zfs.enable; + message = "storage.impermanence can not be used without storage.zfs."; + } + ]; + environment.persistence = lib.mapAttrs (datasetName: dataset: { enable = true; @@ -77,8 +84,12 @@ in { }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); }) config.storage.impermanence.datasets; + # TODO: need for boot on filesystems } (lib.mkIf config.storage.zfs.enable { + # TODO: activationScripts config for private folders + # TODO: rollback post resume + # TODO: fuse userAllowOther storage.zfs.datasets = lib.mapAttrs ( datasetName: dataset: diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 1c1986a..e1f013d 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -23,7 +23,7 @@ }; "persist/system/var/log" = { type = "zfs_fs"; - mountpoint = "/persist/system/var/log"; + mountpoint = "/var/log"; options = { "com.sun:auto-snapshot" = "false"; }; @@ -31,7 +31,6 @@ }; } (util.mkUnless config.storage.impermanence.enable { - # TODO: configure datasets for normal zfs # TODO: create datasets for systemd.services..storage.impermanence.datasets storage.zfs.datasets = { "persist/system/root" = { @@ -48,7 +47,6 @@ }; }; storage.zfs.datasets = { - # TODO: is there a way that we can link these two folders in configs via storage.impermanence.datasets "local/system/root" = { type = "zfs_fs"; mountpoint = "/"; @@ -58,6 +56,14 @@ postCreateHook = '' zfs snapshot rpool/local/system/root@blank ''; + + directories = { + "/var/lib/nixos".enable = true; + "/var/lib/systemd/coredump".enable = true; + }; + files = { + "/etc/machine-id".enable = true; + }; }; }; From 9df29cc07f5592e3c61e924856ceb872f30db2b2 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 13:21:01 -0600 Subject: [PATCH 07/44] feat: refined options for datasets --- .../nixos/defiant/configuration.nix | 85 +++--- modules/nixos-modules/storage/default.nix | 1 + modules/nixos-modules/storage/storage.nix | 64 ++-- .../storage/submodules/dataset.nix | 117 +++---- .../submodules/impermanenceDataset.nix | 11 +- modules/nixos-modules/storage/zfs.nix | 288 +++++++++--------- 6 files changed, 295 insertions(+), 271 deletions(-) diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index e2f9401..11a6f9d 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -33,44 +33,6 @@ isPrincipleUser = true; }; }; - impermanence.enable = true; - storage = { - enable = true; - encryption = true; - notifications = { - enable = true; - host = "smtp.protonmail.ch"; - port = 587; - to = "leyla@jan-leila.com"; - user = "noreply@jan-leila.com"; - tokenFile = config.sops.secrets."services/zfs_smtp_token".path; - }; - pool = { - # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA - bootDrives = ["nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"]; - vdevs = [ - [ - "ata-ST18000NE000-3G6101_ZVTCXVEB" - "ata-ST18000NE000-3G6101_ZVTCXWSC" - "ata-ST18000NE000-3G6101_ZVTD10EH" - "ata-ST18000NT001-3NF101_ZVTE0S3Q" - "ata-ST18000NT001-3NF101_ZVTEF27J" - "ata-ST18000NE000-3G6101_ZVTJ7359" - ] - [ - "ata-ST4000NE001-2MA101_WS2275P3" - "ata-ST4000NE001-2MA101_WS227B9F" - "ata-ST4000NE001-2MA101_WS227CEW" - "ata-ST4000NE001-2MA101_WS227CYN" - "ata-ST4000NE001-2MA101_WS23TBWV" - "ata-ST4000NE001-2MA101_WS23TC5F" - ] - ]; - cache = [ - "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F" - ]; - }; - }; network_storage = { enable = true; directories = [ @@ -104,6 +66,53 @@ }; }; + storage = { + zfs = { + enable = true; + notifications = { + enable = true; + host = "smtp.protonmail.ch"; + port = 587; + to = "leyla@jan-leila.com"; + user = "noreply@jan-leila.com"; + tokenFile = config.sops.secrets."services/zfs_smtp_token".path; + }; + pool = { + encryption = { + enable = true; + }; + vdevs = [ + [ + "ata-ST18000NE000-3G6101_ZVTCXVEB" + "ata-ST18000NE000-3G6101_ZVTCXWSC" + "ata-ST18000NE000-3G6101_ZVTD10EH" + "ata-ST18000NT001-3NF101_ZVTE0S3Q" + "ata-ST18000NT001-3NF101_ZVTEF27J" + "ata-ST18000NE000-3G6101_ZVTJ7359" + ] + [ + "ata-ST4000NE001-2MA101_WS2275P3" + "ata-ST4000NE001-2MA101_WS227B9F" + "ata-ST4000NE001-2MA101_WS227CEW" + "ata-ST4000NE001-2MA101_WS227CYN" + "ata-ST4000NE001-2MA101_WS23TBWV" + "ata-ST4000NE001-2MA101_WS23TC5F" + ] + ]; + # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA + cache = { + cache0 = { + device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"; + boot = true; + }; + }; + }; + }; + impermanence = { + enable = true; + }; + }; + systemd.network = { enable = true; diff --git a/modules/nixos-modules/storage/default.nix b/modules/nixos-modules/storage/default.nix index 02f7fb9..ebf990a 100644 --- a/modules/nixos-modules/storage/default.nix +++ b/modules/nixos-modules/storage/default.nix @@ -8,5 +8,6 @@ imports = [ ./impermanence.nix ./zfs.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index e1f013d..06e29f1 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -1,7 +1,6 @@ { lib, config, - util, ... }: { # TODO: create all of the datasets from option and home-manager datasets @@ -13,50 +12,49 @@ storage.zfs.datasets = { "persist/system/nix" = { type = "zfs_fs"; - mountpoint = "/nix"; - options = { - atime = "off"; - relatime = "off"; - canmount = "on"; - "com.sun:auto-snapshot" = "false"; + mount = { + enable = true; + mountPoint = "/nix"; }; + snapshot = { + autoSnapshot = false; + }; + atime = "off"; + relatime = "off"; }; "persist/system/var/log" = { type = "zfs_fs"; - mountpoint = "/var/log"; - options = { - "com.sun:auto-snapshot" = "false"; + mount = { + enable = true; + mountPoint = "/var/log"; + }; + snapshot = { + autoSnapshot = false; + }; + }; + "persist/system/root" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/"; }; }; }; } - (util.mkUnless config.storage.impermanence.enable { + (lib.mkIf (!config.storage.impermanence.enable) { # TODO: create datasets for systemd.services..storage.impermanence.datasets storage.zfs.datasets = { "persist/system/root" = { type = "zfs_fs"; - mountpoint = "/"; - canmount = "on"; + snapshot = { + autoSnapshot = true; + }; }; }; }) (lib.mkIf config.storage.impermanence.enable { storage.impermanence.datasets = { "persist/system/root" = { - type = "zfs_fs"; - }; - }; - storage.zfs.datasets = { - "local/system/root" = { - type = "zfs_fs"; - mountpoint = "/"; - options = { - canmount = "on"; - }; - postCreateHook = '' - zfs snapshot rpool/local/system/root@blank - ''; - directories = { "/var/lib/nixos".enable = true; "/var/lib/systemd/coredump".enable = true; @@ -66,6 +64,18 @@ }; }; }; + storage.zfs.datasets = { + "local/system/root" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/"; + }; + snapshot = { + blankSnapshot = true; + }; + }; + }; # TODO: home-manager.users..storage.impermanence.enable # is false then persist the entire directory of the user diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index 482671e..a3102fc 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -6,25 +6,6 @@ description = "Type of ZFS dataset (filesystem or volume)"; }; - # ZFS dataset options that match what's currently hardcoded in rootFsOptions - canmount = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "noauto"]); - default = null; - description = "Controls whether the file system can be mounted"; - }; - - mountpoint = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Controls the mount point used for this file system"; - }; - - xattr = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); - default = null; - description = "Extended attribute storage method"; - }; - acltype = lib.mkOption { type = lib.types.nullOr (lib.types.enum ["off" "nfsv4" "posixacl"]); default = null; @@ -37,56 +18,82 @@ description = "Controls when access time is updated"; }; + atime = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off"]); + default = null; + description = "Controls whether access time is updated"; + }; + + xattr = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); + default = null; + description = "Extended attribute storage method"; + }; + compression = lib.mkOption { type = lib.types.nullOr (lib.types.enum ["on" "off" "lz4" "gzip" "zstd" "lzjb" "zle"]); default = null; description = "Compression algorithm to use"; }; - encryption = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]); - default = null; - description = "Encryption algorithm to use"; - }; - - keyformat = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["raw" "hex" "passphrase"]); - default = null; - description = "Format of the encryption key"; - }; - - keylocation = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Location of the encryption key"; - }; - - autoSnapshot = lib.mkOption { - type = lib.types.nullOr lib.types.bool; - default = null; - description = "Enable automatic snapshots for this dataset"; - }; - - # Additional common ZFS options - recordsize = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Suggested block size for files in the file system"; - }; - sync = lib.mkOption { type = lib.types.nullOr (lib.types.enum ["standard" "always" "disabled"]); default = null; description = "Synchronous write behavior"; }; - atime = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off"]); - default = null; - description = "Controls whether access time is updated"; + mount = { + enable = lib.mkOption { + type = lib.types.nullOr (lib.types.either lib.types.bool (lib.types.enum ["on" "off" "noauto"])); + default = null; + }; + mountPoint = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Controls the mount point used for this file system"; + }; + }; + + encryption = { + enable = lib.mkEnableOption "should encryption be enabled"; + type = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]); + default = null; + description = "What encryption type to use"; + }; + keyformat = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["raw" "hex" "passphrase"]); + default = null; + description = "Format of the encryption key"; + }; + keylocation = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Location of the encryption key"; + }; + }; + + snapshot = { + # This option should set this option flag + # "com.sun:auto-snapshot" = "false"; + autoSnapshot = lib.mkOption { + type = lib.types.nullOr lib.types.bool; + default = null; + description = "Enable automatic snapshots for this dataset"; + }; + # TODO: this is what blank snapshot should set + # postCreateHook = '' + # zfs snapshot rpool/local/system/root@blank + # ''; + blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; + }; + + recordSize = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Suggested block size for files in the file system"; }; - # Custom options for disko integration postCreateHook = lib.mkOption { type = lib.types.str; default = ""; diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 2169ec1..5f47c18 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -1,10 +1,5 @@ -args @ { - lib, - name, - ... -}: {...}: let +args @ {lib, ...}: {name, ...}: let datasetSubmodule = (import ./dataset.nix) args; - pathPermissions = { read = lib.mkEnableOption "should the path have read permissions"; write = lib.mkEnableOption "should the path have read permissions"; @@ -52,6 +47,8 @@ in { }; config = { - mountpoint = "/${name}"; + mount = { + mountPoint = lib.mkDefault "/${name}"; + }; }; } diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index bf0c609..fb69f2e 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -171,153 +171,153 @@ in { }; # Disko configuration based on pool settings - disko.devices = { - disk = ( - builtins.listToAttrs ( - builtins.map - (drive: - lib.attrsets.nameValuePair (drive.name) { - type = "disk"; - device = "/dev/disk/by-id/${drive.value}"; - content = { - type = "gpt"; - partitions = { - ESP = lib.mkIf (builtins.elem drive.value bootDrives) { - size = config.storage.zfs.pool.bootPartitionSize; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = ["umask=0077"]; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "rpool"; - }; - }; - }; - }; - }) - allDrives - ) - ); - zpool = { - rpool = { - type = "zpool"; - mode = { - topology = { - type = "topology"; - vdev = ( - builtins.map (disks: { - mode = config.storage.zfs.pool.mode; - members = - builtins.map (disk: disk.name) disks; - }) - poolVdevs - ); - cache = builtins.map (disk: disk.name) poolCache; - }; - }; + # disko.devices = { + # disk = ( + # builtins.listToAttrs ( + # builtins.map + # (drive: + # lib.attrsets.nameValuePair (drive.name) { + # type = "disk"; + # device = "/dev/disk/by-id/${drive.value}"; + # content = { + # type = "gpt"; + # partitions = { + # ESP = lib.mkIf (builtins.elem drive.value bootDrives) { + # size = config.storage.zfs.pool.bootPartitionSize; + # type = "EF00"; + # content = { + # type = "filesystem"; + # format = "vfat"; + # mountpoint = "/boot"; + # mountOptions = ["umask=0077"]; + # }; + # }; + # zfs = { + # size = "100%"; + # content = { + # type = "zfs"; + # pool = "rpool"; + # }; + # }; + # }; + # }; + # }) + # allDrives + # ) + # ); + # zpool = { + # rpool = { + # type = "zpool"; + # mode = { + # topology = { + # type = "topology"; + # vdev = ( + # builtins.map (disks: { + # mode = config.storage.zfs.pool.mode; + # members = + # builtins.map (disk: disk.name) disks; + # }) + # poolVdevs + # ); + # cache = builtins.map (disk: disk.name) poolCache; + # }; + # }; - options = { - ashift = "12"; - autotrim = "on"; - }; + # options = { + # ashift = "12"; + # autotrim = "on"; + # }; - rootFsOptions = let - rootDataset = config.storage.zfs.rootDataset; - # Start with defaults that match the original hardcoded values - defaults = { - canmount = "off"; - mountpoint = "none"; - xattr = "sa"; - acltype = "posixacl"; - relatime = "on"; - compression = "lz4"; - "com.sun:auto-snapshot" = "false"; - }; - # Override defaults with non-null values from rootDataset - userOptions = lib.attrsets.filterAttrs (_: v: v != null) { - canmount = rootDataset.canmount; - mountpoint = rootDataset.mountpoint; - xattr = rootDataset.xattr; - acltype = rootDataset.acltype; - relatime = rootDataset.relatime; - compression = rootDataset.compression; - encryption = rootDataset.encryption; - keyformat = rootDataset.keyformat; - keylocation = rootDataset.keylocation; - recordsize = rootDataset.recordsize; - sync = rootDataset.sync; - atime = rootDataset.atime; - "com.sun:auto-snapshot" = - if rootDataset.autoSnapshot == null - then null - else - ( - if rootDataset.autoSnapshot - then "true" - else "false" - ); - }; - # Only apply pool encryption if user hasn't set encryption options in rootDataset - poolEncryptionOptions = - lib.attrsets.optionalAttrs ( - config.storage.zfs.pool.encryption.enable - && rootDataset.encryption == null - && rootDataset.keyformat == null - && rootDataset.keylocation == null - ) { - encryption = "on"; - keyformat = config.storage.zfs.pool.encryption.keyformat; - keylocation = config.storage.zfs.pool.encryption.keylocation; - }; - in - defaults // userOptions // rootDataset.options // poolEncryptionOptions; + # rootFsOptions = let + # rootDataset = config.storage.zfs.rootDataset; + # # Start with defaults that match the original hardcoded values + # defaults = { + # canmount = "off"; + # mountpoint = "none"; + # xattr = "sa"; + # acltype = "posixacl"; + # relatime = "on"; + # compression = "lz4"; + # "com.sun:auto-snapshot" = "false"; + # }; + # # Override defaults with non-null values from rootDataset + # userOptions = lib.attrsets.filterAttrs (_: v: v != null) { + # canmount = rootDataset.canmount; + # mountpoint = rootDataset.mountpoint; + # xattr = rootDataset.xattr; + # acltype = rootDataset.acltype; + # relatime = rootDataset.relatime; + # compression = rootDataset.compression; + # encryption = rootDataset.encryption; + # keyformat = rootDataset.keyformat; + # keylocation = rootDataset.keylocation; + # recordsize = rootDataset.recordsize; + # sync = rootDataset.sync; + # atime = rootDataset.atime; + # "com.sun:auto-snapshot" = + # if rootDataset.autoSnapshot == null + # then null + # else + # ( + # if rootDataset.autoSnapshot + # then "true" + # else "false" + # ); + # }; + # # Only apply pool encryption if user hasn't set encryption options in rootDataset + # poolEncryptionOptions = + # lib.attrsets.optionalAttrs ( + # config.storage.zfs.pool.encryption.enable + # && rootDataset.encryption == null + # && rootDataset.keyformat == null + # && rootDataset.keylocation == null + # ) { + # encryption = "on"; + # keyformat = config.storage.zfs.pool.encryption.keyformat; + # keylocation = config.storage.zfs.pool.encryption.keylocation; + # }; + # in + # defaults // userOptions // rootDataset.options // poolEncryptionOptions; - datasets = lib.mkMerge [ - ( - lib.attrsets.mapAttrs (name: value: { - type = value.type; - options = let - # For datasets, only include non-null user-specified values - userOptions = lib.attrsets.filterAttrs (_: v: v != null) { - canmount = value.canmount; - xattr = value.xattr; - acltype = value.acltype; - relatime = value.relatime; - compression = value.compression; - encryption = value.encryption; - keyformat = value.keyformat; - keylocation = value.keylocation; - recordsize = value.recordsize; - sync = value.sync; - atime = value.atime; - "com.sun:auto-snapshot" = - if value.autoSnapshot == null - then null - else - ( - if value.autoSnapshot - then "true" - else "false" - ); - }; - in - userOptions // (value.options or {}); - mountpoint = value.mountpoint; - postCreateHook = value.postCreateHook or ""; - }) - config.storage.zfs.datasets - ) - ]; - }; - }; - }; + # datasets = lib.mkMerge [ + # ( + # lib.attrsets.mapAttrs (name: value: { + # type = value.type; + # options = let + # # For datasets, only include non-null user-specified values + # userOptions = lib.attrsets.filterAttrs (_: v: v != null) { + # canmount = value.canmount; + # xattr = value.xattr; + # acltype = value.acltype; + # relatime = value.relatime; + # compression = value.compression; + # encryption = value.encryption; + # keyformat = value.keyformat; + # keylocation = value.keylocation; + # recordsize = value.recordsize; + # sync = value.sync; + # atime = value.atime; + # "com.sun:auto-snapshot" = + # if value.autoSnapshot == null + # then null + # else + # ( + # if value.autoSnapshot + # then "true" + # else "false" + # ); + # }; + # in + # userOptions // (value.options or {}); + # mountpoint = value.mountpoint; + # postCreateHook = value.postCreateHook or ""; + # }) + # config.storage.zfs.datasets + # ) + # ]; + # }; + # }; + # }; # Post-activation scripts for validation system.activationScripts = { From 39edb655390fc1f6ca43c28082a0171abf33ea3a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 14:21:22 -0600 Subject: [PATCH 08/44] feat: removed broken disko config --- modules/nixos-modules/storage/zfs.nix | 428 +------------------------- 1 file changed, 4 insertions(+), 424 deletions(-) diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index fb69f2e..65ddbd0 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -5,52 +5,6 @@ args @ { ... }: let datasetSubmodule = (import ./submodules/dataset.nix) args; - - # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that - hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); - - poolVdevs = - builtins.map ( - vdev: - builtins.map ( - device: let - deviceStr = - if builtins.isString device - then device - else device.device; - in - lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr - ) - vdev - ) - config.storage.zfs.pool.vdevs; - - poolCache = builtins.map ( - name: let - device = config.storage.zfs.pool.cache.${name}; - deviceStr = - if builtins.isString device - then device - else device.device; - in - lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr - ) (builtins.attrNames config.storage.zfs.pool.cache); - - bootDrives = - builtins.map ( - device: - if builtins.isString device - then device - else device.device - ) (builtins.filter ( - device: - if builtins.isString device - then false - else device.boot - ) - (lib.lists.flatten config.storage.zfs.pool.vdevs)); - - allDrives = (lib.lists.flatten poolVdevs) ++ poolCache; in { options.storage = { zfs = { @@ -142,389 +96,15 @@ in { config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { - assertions = [ - { - assertion = builtins.length bootDrives > 0; - message = '' - ZFS configuration requires at least one boot drive. Please configure at least one device with boot = true in storage.zfs.pool.vdevs. - ''; - } - { - assertion = - !( - config.storage.zfs.pool.encryption.enable - && (config.storage.zfs.rootDataset.encryption - != null - || config.storage.zfs.rootDataset.keyformat != null - || config.storage.zfs.rootDataset.keylocation != null) - ); - message = '' - Cannot set encryption options in both pool.encryption and rootDataset. - Use either pool.encryption for default settings or rootDataset encryption options for explicit control, but not both. - ''; - } - ]; - services.zfs = { autoScrub.enable = true; autoSnapshot.enable = true; }; - # Disko configuration based on pool settings - # disko.devices = { - # disk = ( - # builtins.listToAttrs ( - # builtins.map - # (drive: - # lib.attrsets.nameValuePair (drive.name) { - # type = "disk"; - # device = "/dev/disk/by-id/${drive.value}"; - # content = { - # type = "gpt"; - # partitions = { - # ESP = lib.mkIf (builtins.elem drive.value bootDrives) { - # size = config.storage.zfs.pool.bootPartitionSize; - # type = "EF00"; - # content = { - # type = "filesystem"; - # format = "vfat"; - # mountpoint = "/boot"; - # mountOptions = ["umask=0077"]; - # }; - # }; - # zfs = { - # size = "100%"; - # content = { - # type = "zfs"; - # pool = "rpool"; - # }; - # }; - # }; - # }; - # }) - # allDrives - # ) - # ); - # zpool = { - # rpool = { - # type = "zpool"; - # mode = { - # topology = { - # type = "topology"; - # vdev = ( - # builtins.map (disks: { - # mode = config.storage.zfs.pool.mode; - # members = - # builtins.map (disk: disk.name) disks; - # }) - # poolVdevs - # ); - # cache = builtins.map (disk: disk.name) poolCache; - # }; - # }; - - # options = { - # ashift = "12"; - # autotrim = "on"; - # }; - - # rootFsOptions = let - # rootDataset = config.storage.zfs.rootDataset; - # # Start with defaults that match the original hardcoded values - # defaults = { - # canmount = "off"; - # mountpoint = "none"; - # xattr = "sa"; - # acltype = "posixacl"; - # relatime = "on"; - # compression = "lz4"; - # "com.sun:auto-snapshot" = "false"; - # }; - # # Override defaults with non-null values from rootDataset - # userOptions = lib.attrsets.filterAttrs (_: v: v != null) { - # canmount = rootDataset.canmount; - # mountpoint = rootDataset.mountpoint; - # xattr = rootDataset.xattr; - # acltype = rootDataset.acltype; - # relatime = rootDataset.relatime; - # compression = rootDataset.compression; - # encryption = rootDataset.encryption; - # keyformat = rootDataset.keyformat; - # keylocation = rootDataset.keylocation; - # recordsize = rootDataset.recordsize; - # sync = rootDataset.sync; - # atime = rootDataset.atime; - # "com.sun:auto-snapshot" = - # if rootDataset.autoSnapshot == null - # then null - # else - # ( - # if rootDataset.autoSnapshot - # then "true" - # else "false" - # ); - # }; - # # Only apply pool encryption if user hasn't set encryption options in rootDataset - # poolEncryptionOptions = - # lib.attrsets.optionalAttrs ( - # config.storage.zfs.pool.encryption.enable - # && rootDataset.encryption == null - # && rootDataset.keyformat == null - # && rootDataset.keylocation == null - # ) { - # encryption = "on"; - # keyformat = config.storage.zfs.pool.encryption.keyformat; - # keylocation = config.storage.zfs.pool.encryption.keylocation; - # }; - # in - # defaults // userOptions // rootDataset.options // poolEncryptionOptions; - - # datasets = lib.mkMerge [ - # ( - # lib.attrsets.mapAttrs (name: value: { - # type = value.type; - # options = let - # # For datasets, only include non-null user-specified values - # userOptions = lib.attrsets.filterAttrs (_: v: v != null) { - # canmount = value.canmount; - # xattr = value.xattr; - # acltype = value.acltype; - # relatime = value.relatime; - # compression = value.compression; - # encryption = value.encryption; - # keyformat = value.keyformat; - # keylocation = value.keylocation; - # recordsize = value.recordsize; - # sync = value.sync; - # atime = value.atime; - # "com.sun:auto-snapshot" = - # if value.autoSnapshot == null - # then null - # else - # ( - # if value.autoSnapshot - # then "true" - # else "false" - # ); - # }; - # in - # userOptions // (value.options or {}); - # mountpoint = value.mountpoint; - # postCreateHook = value.postCreateHook or ""; - # }) - # config.storage.zfs.datasets - # ) - # ]; - # }; - # }; - # }; - - # Post-activation scripts for validation - system.activationScripts = { - # Script 1: Validate pool, cache devices, and vdevs - zfs-pool-validation = { - text = '' - echo "Running ZFS pool validation..." - - # Function to check if a device exists in a vdev or cache - check_device_in_pool() { - local device_id="$1" - local device_type="$2" # "cache" or "vdev" - - if ! zpool status rpool | grep -q "$device_id"; then - echo "ERROR: Device $device_id not found in pool rpool ($device_type)" - return 1 - fi - return 0 - } - - # Function to validate vdev configuration - validate_vdevs() { - local expected_mode="${config.storage.zfs.pool.mode}" - local pool_status=$(zpool status rpool) - - # Check if pool exists - if ! zpool list rpool >/dev/null 2>&1; then - echo "ERROR: ZFS pool 'rpool' does not exist" - return 1 - fi - - # Validate each configured vdev device - ${lib.concatMapStringsSep "\n" ( - device: let - deviceStr = - if builtins.isString device - then device - else device.device; - in '' - if ! check_device_in_pool "${deviceStr}" "vdev"; then - echo "ERROR: Vdev device ${deviceStr} not found in pool" - exit 1 - fi - '' - ) - (lib.lists.flatten config.storage.zfs.pool.vdevs)} - - # Check pool mode matches configuration - if ! echo "$pool_status" | grep -q "$expected_mode"; then - echo "WARNING: Pool mode may not match expected configuration ($expected_mode)" - fi - - echo "✓ All vdev devices validated successfully" - return 0 - } - - # Function to validate cache configuration - validate_cache() { - ${lib.concatMapStringsSep "\n" ( - name: let - device = config.storage.zfs.pool.cache.${name}; - deviceStr = - if builtins.isString device - then device - else device.device; - in '' - if ! check_device_in_pool "${deviceStr}" "cache"; then - echo "ERROR: Cache device ${deviceStr} (${name}) not found in pool" - exit 1 - fi - '' - ) (builtins.attrNames config.storage.zfs.pool.cache)} - - echo "✓ All cache devices validated successfully" - return 0 - } - - # Run validations - if validate_vdevs && validate_cache; then - echo "✓ ZFS pool validation completed successfully" - else - echo "✗ ZFS pool validation failed" - exit 1 - fi - ''; - deps = ["zfs"]; - }; - - # Script 2: Validate datasets and their options - zfs-dataset-validation = { - text = '' - echo "Running ZFS dataset validation..." - - # Function to check if dataset exists - check_dataset_exists() { - local dataset="$1" - if ! zfs list "$dataset" >/dev/null 2>&1; then - echo "ERROR: Dataset $dataset does not exist" - return 1 - fi - return 0 - } - - # Function to validate dataset options - validate_dataset_options() { - local dataset="$1" - local expected_options="$2" - - # Parse expected options (format: "option=value option2=value2") - echo "$expected_options" | tr ' ' '\n' | while IFS='=' read -r option expected_value; do - if [ -n "$option" ] && [ -n "$expected_value" ]; then - local actual_value=$(zfs get -H -o value "$option" "$dataset" 2>/dev/null) - if [ "$actual_value" != "$expected_value" ]; then - echo "ERROR: Dataset $dataset option $option is '$actual_value', expected '$expected_value'" - return 1 - fi - fi - done - return 0 - } - - # Validate root dataset - echo "Validating root dataset..." - if check_dataset_exists "rpool"; then - root_options="" - ${lib.concatMapStringsSep "\n" ( - option: let - value = config.storage.zfs.rootDataset.${option}; - in - lib.optionalString (value != null) '' - root_options="$root_options ${option}=${toString value}" - '' - ) ["canmount" "xattr" "acltype" "relatime" "compression" "encryption" "keyformat" "keylocation" "recordsize" "sync" "atime"]} - - # Add autoSnapshot option - ${lib.optionalString (config.storage.zfs.rootDataset.autoSnapshot != null) '' - root_options="$root_options com.sun:auto-snapshot=${ - if config.storage.zfs.rootDataset.autoSnapshot - then "true" - else "false" - }" - ''} - - if validate_dataset_options "rpool" "$root_options"; then - echo "✓ Root dataset options validated" - else - echo "✗ Root dataset validation failed" - exit 1 - fi - else - echo "✗ Root dataset validation failed" - exit 1 - fi - - # Validate configured datasets - ${lib.concatMapStringsSep "\n" ( - name: let - dataset = config.storage.zfs.datasets.${name}; - in '' - echo "Validating dataset: rpool/${name}" - if check_dataset_exists "rpool/${name}"; then - dataset_options="" - ${lib.concatMapStringsSep "\n" ( - option: let - value = dataset.${option}; - in - lib.optionalString (value != null) '' - dataset_options="$dataset_options ${option}=${toString value}" - '' - ) ["canmount" "xattr" "acltype" "relatime" "compression" "encryption" "keyformat" "keylocation" "recordsize" "sync" "atime"]} - - # Add autoSnapshot option - ${lib.optionalString (dataset.autoSnapshot != null) '' - dataset_options="$dataset_options com.sun:auto-snapshot=${ - if dataset.autoSnapshot - then "true" - else "false" - }" - ''} - - # Add custom options - ${lib.concatMapStringsSep "\n" ( - optName: let - optValue = dataset.options.${optName}; - in '' - dataset_options="$dataset_options ${optName}=${toString optValue}" - '' - ) (builtins.attrNames (dataset.options or {}))} - - if validate_dataset_options "rpool/${name}" "$dataset_options"; then - echo "✓ Dataset rpool/${name} options validated" - else - echo "✗ Dataset rpool/${name} validation failed" - exit 1 - fi - else - echo "✗ Dataset rpool/${name} validation failed" - exit 1 - fi - '' - ) (builtins.attrNames config.storage.zfs.datasets)} - - echo "✓ ZFS dataset validation completed successfully" - ''; - deps = ["zfs" "zfs-pool-validation"]; - }; - }; + # TODO: configure disko + # TODO: assertion that we have a boot device + # TODO: check that disks on system match configuration and warn user if they don't + # TODO: check that datasets on system match configuration and warn user if they don't } (lib.mkIf config.storage.zfs.notifications.enable { programs.msmtp = { From 3ca0e9bf0a3166741c634b0e6d0ba300769d9317 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 17:04:53 -0600 Subject: [PATCH 09/44] fix: fixed generation of disko configuration --- .../nixos-modules/storage/impermanence.nix | 9 +- modules/nixos-modules/storage/storage.nix | 15 +- .../storage/submodules/dataset.nix | 26 +-- .../submodules/impermanenceDataset.nix | 1 + modules/nixos-modules/storage/zfs.nix | 220 ++++++++++++++++-- 5 files changed, 229 insertions(+), 42 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 470ce48..6619bc5 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -70,6 +70,7 @@ in { lib.mapAttrs (datasetName: dataset: { enable = true; hideMounts = true; + persistentStoragePath = "/${datasetName}"; directories = lib.mapAttrsToList (path: dirConfig: { directory = path; user = dirConfig.owner.name; @@ -78,9 +79,11 @@ in { }) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories); files = lib.mapAttrsToList (path: fileConfig: { file = path; - user = fileConfig.owner.name; - group = fileConfig.group.name; - mode = permissionsToMode fileConfig; + parentDirectory = { + user = fileConfig.owner.name; + group = fileConfig.group.name; + mode = permissionsToMode fileConfig; + }; }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); }) config.storage.impermanence.datasets; diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 06e29f1..b6428f6 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -32,13 +32,6 @@ autoSnapshot = false; }; }; - "persist/system/root" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/"; - }; - }; }; } (lib.mkIf (!config.storage.impermanence.enable) { @@ -46,6 +39,10 @@ storage.zfs.datasets = { "persist/system/root" = { type = "zfs_fs"; + mount = { + enable = false; + mountPoint = "/"; + }; snapshot = { autoSnapshot = true; }; @@ -55,6 +52,10 @@ (lib.mkIf config.storage.impermanence.enable { storage.impermanence.datasets = { "persist/system/root" = { + mount = { + enable = false; + mountPoint = "/"; + }; directories = { "/var/lib/nixos".enable = true; "/var/lib/systemd/coredump".enable = true; diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index a3102fc..3de7719 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -44,12 +44,12 @@ mount = { enable = lib.mkOption { - type = lib.types.nullOr (lib.types.either lib.types.bool (lib.types.enum ["on" "off" "noauto"])); - default = null; + type = lib.types.either lib.types.bool (lib.types.enum ["on" "off" "noauto"]); + default = true; + description = "Whether and how the dataset should be mounted"; }; mountPoint = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; + type = lib.types.str; description = "Controls the mount point used for this file system"; }; }; @@ -57,18 +57,15 @@ encryption = { enable = lib.mkEnableOption "should encryption be enabled"; type = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]); - default = null; + type = lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]; description = "What encryption type to use"; }; keyformat = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["raw" "hex" "passphrase"]); - default = null; + type = lib.types.enum ["raw" "hex" "passphrase"]; description = "Format of the encryption key"; }; keylocation = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; + type = lib.types.str; description = "Location of the encryption key"; }; }; @@ -77,14 +74,11 @@ # This option should set this option flag # "com.sun:auto-snapshot" = "false"; autoSnapshot = lib.mkOption { - type = lib.types.nullOr lib.types.bool; - default = null; + type = lib.types.bool; + default = false; description = "Enable automatic snapshots for this dataset"; }; - # TODO: this is what blank snapshot should set - # postCreateHook = '' - # zfs snapshot rpool/local/system/root@blank - # ''; + # Creates a blank snapshot in the post create hook for rollback purposes blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; }; diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 5f47c18..7154e90 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -49,6 +49,7 @@ in { config = { mount = { mountPoint = lib.mkDefault "/${name}"; + enable = lib.mkDefault true; }; }; } diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 65ddbd0..451e226 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -5,6 +5,98 @@ args @ { ... }: let datasetSubmodule = (import ./submodules/dataset.nix) args; + + # Hash function for disk names (max 27 chars to fit GPT limitations) + hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + + # Helper to flatten vdevs into list of devices with names + allVdevDevices = lib.lists.flatten (builtins.map ( + vdev: + builtins.map ( + device: + lib.attrsets.nameValuePair (hashDisk device.device) device + ) + vdev + ) + config.storage.zfs.pool.vdevs); + + # Cache devices with names + allCacheDevices = builtins.map ( + device: + lib.attrsets.nameValuePair (hashDisk device.device) device + ) (config.storage.zfs.pool.cache); + + # All devices (vdevs + cache) + allDevices = allVdevDevices ++ allCacheDevices; + + # Boot devices - filter devices that have boot = true + bootDevices = builtins.filter (device: device.value.boot) allDevices; + + # Helper function to convert dataset options to ZFS properties + datasetToZfsOptions = dataset: let + baseOptions = + (lib.attrsets.optionalAttrs (dataset.acltype != null) {acltype = dataset.acltype;}) + // (lib.attrsets.optionalAttrs (dataset.relatime != null) {relatime = dataset.relatime;}) + // (lib.attrsets.optionalAttrs (dataset.atime != null) {atime = dataset.atime;}) + // (lib.attrsets.optionalAttrs (dataset.xattr != null) {xattr = dataset.xattr;}) + // (lib.attrsets.optionalAttrs (dataset.compression != null) {compression = dataset.compression;}) + // (lib.attrsets.optionalAttrs (dataset.sync != null) {sync = dataset.sync;}) + // (lib.attrsets.optionalAttrs (dataset.recordSize != null) {recordSize = dataset.recordSize;}); + + encryptionOptions = lib.attrsets.optionalAttrs (dataset.encryption.enable) ( + (lib.attrsets.optionalAttrs (dataset.encryption ? type) {encryption = dataset.encryption.type;}) + // (lib.attrsets.optionalAttrs (dataset.encryption ? keyformat) {keyformat = dataset.encryption.keyformat;}) + // (lib.attrsets.optionalAttrs (dataset.encryption ? keylocation) {keylocation = dataset.encryption.keylocation;}) + ); + + mountOptions = lib.attrsets.optionalAttrs (dataset ? mount && dataset.mount ? enable) ( + if builtins.isBool dataset.mount.enable + then { + canmount = + if dataset.mount.enable + then "on" + else "off"; + } + else {canmount = dataset.mount.enable;} + ); + + snapshotOptions = lib.attrsets.optionalAttrs (dataset ? snapshot && dataset.snapshot ? autoSnapshot) { + "com.sun:auto-snapshot" = + if dataset.snapshot.autoSnapshot + then "true" + else "false"; + }; + in + baseOptions // encryptionOptions // mountOptions // snapshotOptions; + + # Helper to generate post create hooks + generatePostCreateHook = name: dataset: + dataset.postCreateHook + + (lib.optionalString dataset.snapshot.blankSnapshot '' + zfs snapshot rpool/${name}@blank + ''); + + # Convert datasets to disko format + convertedDatasets = builtins.listToAttrs ( + (lib.attrsets.mapAttrsToList ( + name: dataset: + lib.attrsets.nameValuePair name { + type = dataset.type; + options = datasetToZfsOptions dataset; + mountpoint = dataset.mount.mountPoint or null; + postCreateHook = generatePostCreateHook name dataset; + } + ) + config.storage.zfs.datasets) + ++ (lib.optional (config.storage.zfs.rootDataset != null) ( + lib.attrsets.nameValuePair "" { + type = config.storage.zfs.rootDataset.type; + options = datasetToZfsOptions config.storage.zfs.rootDataset; + mountpoint = config.storage.zfs.rootDataset.mount.mountPoint or null; + postCreateHook = generatePostCreateHook "" config.storage.zfs.rootDataset; + } + )) + ); in { options.storage = { zfs = { @@ -39,12 +131,14 @@ in { lib.types.coercedTo lib.types.str (device: { device = device; boot = false; - }) { - device = lib.mkOption { - type = lib.types.str; + }) (lib.types.submodule { + options = { + device = lib.mkOption { + type = lib.types.str; + }; + boot = lib.mkEnableOption "should this device be a boot device"; }; - boot = lib.mkEnableOption "should this device be a boot device"; - }; + }); in { encryption = { enable = lib.mkEnableOption "Should encryption be enabled on this pool."; @@ -75,15 +169,15 @@ in { description = "List of vdevs, where each vdev is a list of devices"; }; cache = lib.mkOption { - type = lib.types.attrsOf deviceType; + type = lib.types.listOf deviceType; default = {}; }; }; rootDataset = lib.mkOption { - type = lib.types.submodule datasetSubmodule; + type = lib.types.nullOr (lib.types.submodule datasetSubmodule); description = "Root ZFS dataset to create"; - default = {}; + default = null; }; datasets = lib.mkOption { @@ -96,15 +190,109 @@ in { config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { - services.zfs = { - autoScrub.enable = true; - autoSnapshot.enable = true; - }; + # Assertion that we have at least one boot device + assertions = [ + { + assertion = (builtins.length bootDevices) > 0; + message = "ZFS configuration requires at least one boot device. Set boot = true for at least one device in your vdevs or cache."; + } + ]; - # TODO: configure disko - # TODO: assertion that we have a boot device - # TODO: check that disks on system match configuration and warn user if they don't - # TODO: check that datasets on system match configuration and warn user if they don't + # # Warning about disk/dataset mismatches - these would be runtime checks + # warnings = let + # configuredDisks = builtins.map (device: device.device) (builtins.map (dev: dev.value) allDevices); + # diskWarnings = + # lib.optional (config.storage.zfs.enable) + # "ZFS: Please ensure the following disks are available on your system: ${builtins.concatStringsSep ", " configuredDisks}"; + + # configuredDatasets = builtins.attrNames config.storage.zfs.datasets; + # datasetWarnings = + # lib.optional (config.storage.zfs.enable && (builtins.length configuredDatasets) > 0) + # "ZFS: Configured datasets: ${builtins.concatStringsSep ", " configuredDatasets}. Ensure these match your intended ZFS layout."; + # in + # diskWarnings ++ datasetWarnings; + + # services.zfs = { + # autoScrub.enable = true; + # autoSnapshot.enable = true; + # }; + + # # Configure disko for ZFS setup + disko.devices = { + disk = builtins.listToAttrs ( + builtins.map ( + drive: + lib.attrsets.nameValuePair (drive.name) { + type = "disk"; + device = "/dev/disk/by-id/${drive.value.device}"; + content = { + type = "gpt"; + partitions = { + ESP = lib.mkIf drive.value.boot { + size = config.storage.zfs.pool.bootPartitionSize; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "rpool"; + }; + }; + }; + }; + } + ) + allDevices + ); + + zpool = { + rpool = { + type = "zpool"; + mode = { + topology = { + type = "topology"; + vdev = + builtins.map (vdev: { + mode = config.storage.zfs.pool.mode; + members = builtins.map (device: hashDisk device.device) vdev; + }) + config.storage.zfs.pool.vdevs; + cache = builtins.map (device: hashDisk device.device) (builtins.attrValues config.storage.zfs.pool.cache); + }; + }; + + options = { + ashift = "12"; + autotrim = "on"; + }; + + rootFsOptions = + { + canmount = "off"; + mountpoint = "none"; + xattr = "sa"; + acltype = "posixacl"; + relatime = "on"; + compression = "lz4"; + "com.sun:auto-snapshot" = "false"; + } + // (lib.attrsets.optionalAttrs config.storage.zfs.pool.encryption.enable { + encryption = "on"; + keyformat = config.storage.zfs.pool.encryption.keyformat; + keylocation = config.storage.zfs.pool.encryption.keylocation; + }); + + datasets = convertedDatasets; + }; + }; + }; } (lib.mkIf config.storage.zfs.notifications.enable { programs.msmtp = { From b67be1472a1b4552b57b1672129899ef8475dd41 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:17:22 -0600 Subject: [PATCH 10/44] feat: refactored impermanence modules to follow new pattern --- .../nixos-modules/server/actual/default.nix | 2 +- .../server/actual/impermanence.nix | 37 --------- .../nixos-modules/server/actual/storage.nix | 41 ++++++++++ .../nixos-modules/server/bazarr/default.nix | 2 +- .../server/bazarr/impermanence.nix | 33 -------- .../nixos-modules/server/bazarr/storage.nix | 36 +++++++++ .../server/crab-hole/default.nix | 2 +- .../server/crab-hole/impermanence.nix | 33 -------- .../server/crab-hole/storage.nix | 37 +++++++++ .../nixos-modules/server/fail2ban/default.nix | 2 +- .../server/fail2ban/impermanence.nix | 34 --------- .../nixos-modules/server/fail2ban/storage.nix | 37 +++++++++ .../server/flaresolverr/default.nix | 2 +- .../server/flaresolverr/impermanence.nix | 26 ------- .../server/flaresolverr/storage.nix | 26 +++++++ .../nixos-modules/server/forgejo/default.nix | 2 +- .../server/forgejo/impermanence.nix | 35 --------- .../nixos-modules/server/forgejo/storage.nix | 36 +++++++++ .../server/home-assistant/default.nix | 2 +- .../server/home-assistant/impermanence.nix | 26 ------- .../server/home-assistant/storage.nix | 36 +++++++++ .../nixos-modules/server/immich/default.nix | 2 +- .../server/immich/impermanence.nix | 32 -------- .../nixos-modules/server/immich/storage.nix | 36 +++++++++ .../nixos-modules/server/jackett/default.nix | 2 +- .../server/jackett/impermanence.nix | 33 -------- .../nixos-modules/server/jackett/storage.nix | 36 +++++++++ .../nixos-modules/server/jellyfin/default.nix | 2 +- .../server/jellyfin/impermanence.nix | 73 ------------------ .../nixos-modules/server/jellyfin/storage.nix | 76 +++++++++++++++++++ .../nixos-modules/server/lidarr/default.nix | 2 +- .../server/lidarr/impermanence.nix | 33 -------- .../nixos-modules/server/lidarr/storage.nix | 36 +++++++++ .../server/panoramax/default.nix | 2 +- .../server/panoramax/impermanence.nix | 20 ----- .../server/panoramax/storage.nix | 33 ++++++++ .../server/paperless/default.nix | 2 +- .../server/paperless/impermanence.nix | 32 -------- .../server/paperless/storage.nix | 36 +++++++++ .../nixos-modules/server/postgres/default.nix | 2 +- .../server/postgres/impermanence.nix | 27 ------- .../nixos-modules/server/postgres/storage.nix | 36 +++++++++ .../server/qbittorent/default.nix | 2 +- .../server/qbittorent/impermanence.nix | 61 --------------- .../server/qbittorent/storage.nix | 62 +++++++++++++++ .../nixos-modules/server/radarr/default.nix | 2 +- .../server/radarr/impermanence.nix | 33 -------- .../nixos-modules/server/radarr/storage.nix | 36 +++++++++ .../server/reverseProxy/default.nix | 2 +- .../server/reverseProxy/impermanence.nix | 21 ----- .../server/reverseProxy/storage.nix | 28 +++++++ .../nixos-modules/server/sonarr/default.nix | 2 +- .../server/sonarr/impermanence.nix | 33 -------- .../nixos-modules/server/sonarr/storage.nix | 36 +++++++++ 54 files changed, 718 insertions(+), 640 deletions(-) delete mode 100644 modules/nixos-modules/server/actual/impermanence.nix create mode 100644 modules/nixos-modules/server/actual/storage.nix delete mode 100644 modules/nixos-modules/server/bazarr/impermanence.nix create mode 100644 modules/nixos-modules/server/bazarr/storage.nix delete mode 100644 modules/nixos-modules/server/crab-hole/impermanence.nix create mode 100644 modules/nixos-modules/server/crab-hole/storage.nix delete mode 100644 modules/nixos-modules/server/fail2ban/impermanence.nix create mode 100644 modules/nixos-modules/server/fail2ban/storage.nix delete mode 100644 modules/nixos-modules/server/flaresolverr/impermanence.nix create mode 100644 modules/nixos-modules/server/flaresolverr/storage.nix delete mode 100644 modules/nixos-modules/server/forgejo/impermanence.nix create mode 100644 modules/nixos-modules/server/forgejo/storage.nix delete mode 100644 modules/nixos-modules/server/home-assistant/impermanence.nix create mode 100644 modules/nixos-modules/server/home-assistant/storage.nix delete mode 100644 modules/nixos-modules/server/immich/impermanence.nix create mode 100644 modules/nixos-modules/server/immich/storage.nix delete mode 100644 modules/nixos-modules/server/jackett/impermanence.nix create mode 100644 modules/nixos-modules/server/jackett/storage.nix delete mode 100644 modules/nixos-modules/server/jellyfin/impermanence.nix create mode 100644 modules/nixos-modules/server/jellyfin/storage.nix delete mode 100644 modules/nixos-modules/server/lidarr/impermanence.nix create mode 100644 modules/nixos-modules/server/lidarr/storage.nix delete mode 100644 modules/nixos-modules/server/panoramax/impermanence.nix create mode 100644 modules/nixos-modules/server/panoramax/storage.nix delete mode 100644 modules/nixos-modules/server/paperless/impermanence.nix create mode 100644 modules/nixos-modules/server/paperless/storage.nix delete mode 100644 modules/nixos-modules/server/postgres/impermanence.nix create mode 100644 modules/nixos-modules/server/postgres/storage.nix delete mode 100644 modules/nixos-modules/server/qbittorent/impermanence.nix create mode 100644 modules/nixos-modules/server/qbittorent/storage.nix delete mode 100644 modules/nixos-modules/server/radarr/impermanence.nix create mode 100644 modules/nixos-modules/server/radarr/storage.nix delete mode 100644 modules/nixos-modules/server/reverseProxy/impermanence.nix create mode 100644 modules/nixos-modules/server/reverseProxy/storage.nix delete mode 100644 modules/nixos-modules/server/sonarr/impermanence.nix create mode 100644 modules/nixos-modules/server/sonarr/storage.nix diff --git a/modules/nixos-modules/server/actual/default.nix b/modules/nixos-modules/server/actual/default.nix index b59517b..99778af 100644 --- a/modules/nixos-modules/server/actual/default.nix +++ b/modules/nixos-modules/server/actual/default.nix @@ -3,6 +3,6 @@ ./actual.nix ./proxy.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/actual/impermanence.nix b/modules/nixos-modules/server/actual/impermanence.nix deleted file mode 100644 index d870789..0000000 --- a/modules/nixos-modules/server/actual/impermanence.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ - lib, - config, - ... -}: let - const = import ./const.nix; - dataDirectory = const.dataDirectory; -in { - options.services.actual = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.actual.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.actual.impermanence.enable { - assertions = [ - { - assertion = config.services.actual.settings.dataDir == dataDirectory; - message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}"; - } - { - assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false; - message = "actual systemd service must have DynamicUser enabled to use private directory"; - } - ]; - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = dataDirectory; - user = "actual"; - group = "actual"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/actual/storage.nix b/modules/nixos-modules/server/actual/storage.nix new file mode 100644 index 0000000..eab0817 --- /dev/null +++ b/modules/nixos-modules/server/actual/storage.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + dataDirectory = const.dataDirectory; +in { + options.services.actual.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.actual.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.actual.settings.dataDir == dataDirectory; + message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}"; + } + { + assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false; + message = "actual systemd service must have DynamicUser enabled to use private directory"; + } + ]; + } + (lib.mkIf (!config.services.actual.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.actual.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${dataDirectory}" = { + owner.name = "actual"; + group.name = "actual"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/bazarr/default.nix b/modules/nixos-modules/server/bazarr/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/bazarr/default.nix +++ b/modules/nixos-modules/server/bazarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/bazarr/impermanence.nix b/modules/nixos-modules/server/bazarr/impermanence.nix deleted file mode 100644 index 70a45d1..0000000 --- a/modules/nixos-modules/server/bazarr/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - bazarr_data_directory = "/var/lib/bazarr"; -in { - options.services.bazarr = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.bazarr.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.bazarr.impermanence.enable { - assertions = [ - { - assertion = config.services.bazarr.dataDir == bazarr_data_directory; - message = "bazarr data directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = bazarr_data_directory; - user = "bazarr"; - group = "bazarr"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/bazarr/storage.nix b/modules/nixos-modules/server/bazarr/storage.nix new file mode 100644 index 0000000..53a9d9c --- /dev/null +++ b/modules/nixos-modules/server/bazarr/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + bazarr_data_directory = "/var/lib/bazarr"; +in { + options.services.bazarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.bazarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.bazarr.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.bazarr.dataDir == bazarr_data_directory; + message = "bazarr data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.bazarr.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.bazarr.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${bazarr_data_directory}" = { + owner.name = "bazarr"; + group.name = "bazarr"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/crab-hole/default.nix b/modules/nixos-modules/server/crab-hole/default.nix index 158a851..9f990c5 100644 --- a/modules/nixos-modules/server/crab-hole/default.nix +++ b/modules/nixos-modules/server/crab-hole/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./crab-hole.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/crab-hole/impermanence.nix b/modules/nixos-modules/server/crab-hole/impermanence.nix deleted file mode 100644 index 51efc0c..0000000 --- a/modules/nixos-modules/server/crab-hole/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - workingDirectory = "/var/lib/private/crab-hole"; -in { - options.services.crab-hole = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.crab-hole.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.crab-hole.impermanence.enable { - assertions = [ - { - assertion = - config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory); - message = "crab-hole working directory does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = workingDirectory; - user = "crab-hole"; - group = "crab-hole"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/crab-hole/storage.nix b/modules/nixos-modules/server/crab-hole/storage.nix new file mode 100644 index 0000000..ec38846 --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/storage.nix @@ -0,0 +1,37 @@ +{ + lib, + config, + ... +}: let + workingDirectory = "/var/lib/private/crab-hole"; +in { + options.services.crab-hole.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.crab-hole.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.crab-hole.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = + config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory); + message = "crab-hole working directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.crab-hole.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.crab-hole.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${workingDirectory}" = { + owner.name = "crab-hole"; + group.name = "crab-hole"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/fail2ban/default.nix b/modules/nixos-modules/server/fail2ban/default.nix index 30fca99..84a46d4 100644 --- a/modules/nixos-modules/server/fail2ban/default.nix +++ b/modules/nixos-modules/server/fail2ban/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/fail2ban/impermanence.nix b/modules/nixos-modules/server/fail2ban/impermanence.nix deleted file mode 100644 index 6e214b3..0000000 --- a/modules/nixos-modules/server/fail2ban/impermanence.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - lib, - config, - ... -}: let - dataFolder = "/var/lib/fail2ban"; - dataFile = "fail2ban.sqlite3"; -in { - options.services.fail2ban = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.fail2ban.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.fail2ban.impermanence.enable { - assertions = [ - { - assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; - message = "fail2ban data file does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = dataFolder; - user = "fail2ban"; - group = "fail2ban"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/fail2ban/storage.nix b/modules/nixos-modules/server/fail2ban/storage.nix new file mode 100644 index 0000000..6c1f227 --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/storage.nix @@ -0,0 +1,37 @@ +{ + lib, + config, + ... +}: let + dataFolder = "/var/lib/fail2ban"; + dataFile = "fail2ban.sqlite3"; +in { + options.services.fail2ban.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.fail2ban.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; + message = "fail2ban data file does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.fail2ban.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.fail2ban.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${dataFolder}" = { + owner.name = "fail2ban"; + group.name = "fail2ban"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/flaresolverr/default.nix b/modules/nixos-modules/server/flaresolverr/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/flaresolverr/default.nix +++ b/modules/nixos-modules/server/flaresolverr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/flaresolverr/impermanence.nix b/modules/nixos-modules/server/flaresolverr/impermanence.nix deleted file mode 100644 index 4544e75..0000000 --- a/modules/nixos-modules/server/flaresolverr/impermanence.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.flaresolverr = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.flaresolverr.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.flaresolverr.impermanence.enable { - # FlareSolverr typically doesn't need persistent storage as it's a proxy service - # but we'll add basic structure in case it's needed for logs or configuration - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = "/var/lib/flaresolverr"; - user = "flaresolverr"; - group = "flaresolverr"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/flaresolverr/storage.nix b/modules/nixos-modules/server/flaresolverr/storage.nix new file mode 100644 index 0000000..657bcc6 --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/storage.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: { + options.services.flaresolverr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.flaresolverr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.flaresolverr.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + (lib.mkIf (!config.services.flaresolverr.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.flaresolverr.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."/var/lib/flaresolverr" = { + owner.name = "flaresolverr"; + group.name = "flaresolverr"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/forgejo/default.nix b/modules/nixos-modules/server/forgejo/default.nix index 4333f69..c990e57 100644 --- a/modules/nixos-modules/server/forgejo/default.nix +++ b/modules/nixos-modules/server/forgejo/default.nix @@ -4,6 +4,6 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/forgejo/impermanence.nix b/modules/nixos-modules/server/forgejo/impermanence.nix deleted file mode 100644 index 6fe3de8..0000000 --- a/modules/nixos-modules/server/forgejo/impermanence.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ - lib, - config, - ... -}: let - stateDir = "/var/lib/forgejo"; -in { - options.services.forgejo = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.forgejo.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.forgejo.impermanence.enable { - assertions = [ - { - assertion = config.services.forgejo.stateDir == stateDir; - message = "forgejo state directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = stateDir; - user = "forgejo"; - group = "forgejo"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/forgejo/storage.nix b/modules/nixos-modules/server/forgejo/storage.nix new file mode 100644 index 0000000..31304e7 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + stateDir = "/var/lib/forgejo"; +in { + options.services.forgejo.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.forgejo.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.forgejo.stateDir == stateDir; + message = "forgejo state directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.forgejo.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.forgejo.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${stateDir}" = { + owner.name = "forgejo"; + group.name = "forgejo"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/home-assistant/default.nix b/modules/nixos-modules/server/home-assistant/default.nix index b6f9356..d213964 100644 --- a/modules/nixos-modules/server/home-assistant/default.nix +++ b/modules/nixos-modules/server/home-assistant/default.nix @@ -4,7 +4,7 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ./extensions ]; } diff --git a/modules/nixos-modules/server/home-assistant/impermanence.nix b/modules/nixos-modules/server/home-assistant/impermanence.nix deleted file mode 100644 index 8c056a1..0000000 --- a/modules/nixos-modules/server/home-assistant/impermanence.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - lib, - config, - ... -}: let - configDir = "/var/lib/hass"; -in - lib.mkIf (config.host.impermanence.enable && config.services.home-assistant.enable) { - assertions = [ - { - assertion = config.services.home-assistant.configDir == configDir; - message = "home assistant config directory does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = configDir; - user = "hass"; - group = "hass"; - } - ]; - }; - } diff --git a/modules/nixos-modules/server/home-assistant/storage.nix b/modules/nixos-modules/server/home-assistant/storage.nix new file mode 100644 index 0000000..231387b --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + configDir = "/var/lib/hass"; +in { + options.services.home-assistant.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.home-assistant.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.home-assistant.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.home-assistant.configDir == configDir; + message = "home assistant config directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.home-assistant.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.home-assistant.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${configDir}" = { + owner.name = "hass"; + group.name = "hass"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/immich/default.nix b/modules/nixos-modules/server/immich/default.nix index 4d93c0b..75ae2fd 100644 --- a/modules/nixos-modules/server/immich/default.nix +++ b/modules/nixos-modules/server/immich/default.nix @@ -3,7 +3,7 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; # NOTE: This shouldn't be needed now that we are out of testing diff --git a/modules/nixos-modules/server/immich/impermanence.nix b/modules/nixos-modules/server/immich/impermanence.nix deleted file mode 100644 index 56e51d0..0000000 --- a/modules/nixos-modules/server/immich/impermanence.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ - lib, - config, - ... -}: let - mediaLocation = "/var/lib/immich"; -in { - options.services.immich = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.immich.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.immich.impermanence.enable { - assertions = [ - { - assertion = config.services.immich.mediaLocation == mediaLocation; - message = "immich media location does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = mediaLocation; - user = "immich"; - group = "immich"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/immich/storage.nix b/modules/nixos-modules/server/immich/storage.nix new file mode 100644 index 0000000..65b4bed --- /dev/null +++ b/modules/nixos-modules/server/immich/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + mediaLocation = "/var/lib/immich"; +in { + options.services.immich.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.immich.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.immich.mediaLocation == mediaLocation; + message = "immich media location does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.immich.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.immich.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${mediaLocation}" = { + owner.name = "immich"; + group.name = "immich"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/jackett/default.nix b/modules/nixos-modules/server/jackett/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/jackett/default.nix +++ b/modules/nixos-modules/server/jackett/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/jackett/impermanence.nix b/modules/nixos-modules/server/jackett/impermanence.nix deleted file mode 100644 index 24fc5e6..0000000 --- a/modules/nixos-modules/server/jackett/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - jackett_data_directory = "/var/lib/jackett/.config/Jackett"; -in { - options.services.jackett = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jackett.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.jackett.impermanence.enable { - assertions = [ - { - assertion = config.services.jackett.dataDir == jackett_data_directory; - message = "jackett data directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = jackett_data_directory; - user = "jackett"; - group = "jackett"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/jackett/storage.nix b/modules/nixos-modules/server/jackett/storage.nix new file mode 100644 index 0000000..6056c9c --- /dev/null +++ b/modules/nixos-modules/server/jackett/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + jackett_data_directory = "/var/lib/jackett/.config/Jackett"; +in { + options.services.jackett.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jackett.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.jackett.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.jackett.dataDir == jackett_data_directory; + message = "jackett data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.jackett.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.jackett.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${jackett_data_directory}" = { + owner.name = "jackett"; + group.name = "jackett"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/jellyfin/default.nix b/modules/nixos-modules/server/jellyfin/default.nix index 2dbdcfd..4770ae1 100644 --- a/modules/nixos-modules/server/jellyfin/default.nix +++ b/modules/nixos-modules/server/jellyfin/default.nix @@ -3,6 +3,6 @@ ./jellyfin.nix ./proxy.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/jellyfin/impermanence.nix b/modules/nixos-modules/server/jellyfin/impermanence.nix deleted file mode 100644 index cbcb54f..0000000 --- a/modules/nixos-modules/server/jellyfin/impermanence.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ - lib, - config, - ... -}: let - jellyfin_data_directory = "/var/lib/jellyfin"; - jellyfin_cache_directory = "/var/cache/jellyfin"; -in { - options.services.jellyfin = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jellyfin.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.jellyfin.impermanence.enable { - fileSystems."/persist/system/jellyfin".neededForBoot = true; - - host.storage.pool.extraDatasets = { - # sops age key needs to be available to pre persist for user generation - "persist/system/jellyfin" = { - type = "zfs_fs"; - mountpoint = "/persist/system/jellyfin"; - options = { - atime = "off"; - relatime = "off"; - canmount = "on"; - }; - }; - }; - - assertions = [ - { - assertion = config.services.jellyfin.dataDir == jellyfin_data_directory; - message = "jellyfin data directory does not match persistence"; - } - { - assertion = config.services.jellyfin.cacheDir == jellyfin_cache_directory; - message = "jellyfin cache directory does not match persistence"; - } - ]; - - environment.persistence = { - "/persist/system/root" = { - directories = [ - { - directory = jellyfin_data_directory; - user = "jellyfin"; - group = "jellyfin"; - } - { - directory = jellyfin_cache_directory; - user = "jellyfin"; - group = "jellyfin"; - } - ]; - }; - - "/persist/system/jellyfin" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.jellyfin.media_directory; - user = "jellyfin"; - group = "jellyfin_media"; - mode = "1770"; - } - ]; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix new file mode 100644 index 0000000..867b936 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -0,0 +1,76 @@ +{ + lib, + config, + ... +}: let + jellyfin_data_directory = "/var/lib/jellyfin"; + jellyfin_cache_directory = "/var/cache/jellyfin"; +in { + options.services.jellyfin.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.jellyfin.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.jellyfin.dataDir == jellyfin_data_directory; + message = "jellyfin data directory does not match persistence"; + } + { + assertion = config.services.jellyfin.cacheDir == jellyfin_cache_directory; + message = "jellyfin cache directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.jellyfin.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.jellyfin.impermanence.enable { + storage.impermanence.datasets = { + "persist/system/root" = { + directories = { + "${jellyfin_data_directory}" = { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; + }; + "${jellyfin_cache_directory}" = { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; + }; + }; + }; + "persist/system/jellyfin" = { + atime = "off"; + relatime = "off"; + + directories."${config.services.jellyfin.media_directory}" = { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin_media"; + owner.permissions = { + read = true; + write = true; + execute = true; + }; + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/lidarr/default.nix b/modules/nixos-modules/server/lidarr/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/lidarr/default.nix +++ b/modules/nixos-modules/server/lidarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/lidarr/impermanence.nix b/modules/nixos-modules/server/lidarr/impermanence.nix deleted file mode 100644 index 5d3aa3f..0000000 --- a/modules/nixos-modules/server/lidarr/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; -in { - options.services.lidarr = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.lidarr.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.lidarr.impermanence.enable { - assertions = [ - { - assertion = config.services.lidarr.dataDir == lidarr_data_directory; - message = "lidarr data directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = lidarr_data_directory; - user = "lidarr"; - group = "lidarr"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/lidarr/storage.nix b/modules/nixos-modules/server/lidarr/storage.nix new file mode 100644 index 0000000..9d818ff --- /dev/null +++ b/modules/nixos-modules/server/lidarr/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; +in { + options.services.lidarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.lidarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.lidarr.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.lidarr.dataDir == lidarr_data_directory; + message = "lidarr data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.lidarr.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.lidarr.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${lidarr_data_directory}" = { + owner.name = "lidarr"; + group.name = "lidarr"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/panoramax/default.nix b/modules/nixos-modules/server/panoramax/default.nix index 4c6b9ea..f5a514f 100644 --- a/modules/nixos-modules/server/panoramax/default.nix +++ b/modules/nixos-modules/server/panoramax/default.nix @@ -2,7 +2,7 @@ imports = [ ./proxy.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ./panoramax.nix ./database.nix ]; diff --git a/modules/nixos-modules/server/panoramax/impermanence.nix b/modules/nixos-modules/server/panoramax/impermanence.nix deleted file mode 100644 index e25ef92..0000000 --- a/modules/nixos-modules/server/panoramax/impermanence.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.panoramax = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.panoramax.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.panoramax.impermanence.enable { - # TODO: configure impermanence for panoramax data - # This would typically include directories like: - # - /var/lib/panoramax - # - panoramax storage directories - # - any cache or temporary directories that need to persist - }; -} diff --git a/modules/nixos-modules/server/panoramax/storage.nix b/modules/nixos-modules/server/panoramax/storage.nix new file mode 100644 index 0000000..52d9d74 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/storage.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # TODO: configure impermanence for panoramax data + # This would typically include directories like: + # - /var/lib/panoramax + # - panoramax storage directories + # - any cache or temporary directories that need to persist + } + (lib.mkIf (!config.services.panoramax.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.panoramax.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."/var/lib/panoramax" = { + owner.name = "panoramax"; + group.name = "panoramax"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/paperless/default.nix b/modules/nixos-modules/server/paperless/default.nix index 7e5e16b..f7a5aa7 100644 --- a/modules/nixos-modules/server/paperless/default.nix +++ b/modules/nixos-modules/server/paperless/default.nix @@ -4,6 +4,6 @@ ./proxy.nix ./database.nix ./fail2ban.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/paperless/impermanence.nix b/modules/nixos-modules/server/paperless/impermanence.nix deleted file mode 100644 index fc87ea7..0000000 --- a/modules/nixos-modules/server/paperless/impermanence.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ - config, - lib, - ... -}: let - dataDir = "/var/lib/paperless"; -in { - options.services.paperless = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.paperless.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.paperless.impermanence.enable { - assertions = [ - { - assertion = config.services.paperless.dataDir == dataDir; - message = "paperless data location does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = dataDir; - user = "paperless"; - group = "paperless"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/paperless/storage.nix b/modules/nixos-modules/server/paperless/storage.nix new file mode 100644 index 0000000..6f74441 --- /dev/null +++ b/modules/nixos-modules/server/paperless/storage.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/paperless"; +in { + options.services.paperless.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.paperless.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.paperless.dataDir == dataDir; + message = "paperless data location does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.paperless.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.paperless.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${dataDir}" = { + owner.name = "paperless"; + group.name = "paperless"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/postgres/default.nix b/modules/nixos-modules/server/postgres/default.nix index abf4ade..50d90d4 100644 --- a/modules/nixos-modules/server/postgres/default.nix +++ b/modules/nixos-modules/server/postgres/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./postgres.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/postgres/impermanence.nix b/modules/nixos-modules/server/postgres/impermanence.nix deleted file mode 100644 index a67fb1a..0000000 --- a/modules/nixos-modules/server/postgres/impermanence.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ - config, - lib, - ... -}: let - dataDir = "/var/lib/postgresql/16"; -in { - config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) { - assertions = [ - { - assertion = config.services.postgresql.dataDir == dataDir; - message = "postgres data directory does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = dataDir; - user = "postgres"; - group = "postgres"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/postgres/storage.nix b/modules/nixos-modules/server/postgres/storage.nix new file mode 100644 index 0000000..0ec0eb2 --- /dev/null +++ b/modules/nixos-modules/server/postgres/storage.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/postgresql/16"; +in { + options.services.postgresql.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.postgresql.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.postgresql.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.postgresql.dataDir == dataDir; + message = "postgres data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.postgresql.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.postgresql.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${dataDir}" = { + owner.name = "postgres"; + group.name = "postgres"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/qbittorent/default.nix b/modules/nixos-modules/server/qbittorent/default.nix index f7511e6..11cc449 100644 --- a/modules/nixos-modules/server/qbittorent/default.nix +++ b/modules/nixos-modules/server/qbittorent/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./qbittorent.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/qbittorent/impermanence.nix b/modules/nixos-modules/server/qbittorent/impermanence.nix deleted file mode 100644 index 1489e7d..0000000 --- a/modules/nixos-modules/server/qbittorent/impermanence.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - lib, - config, - ... -}: let - qbittorent_profile_directory = "/var/lib/qBittorrent/"; -in { - options.services.qbittorrent = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.qbittorrent.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.qbittorrent.impermanence.enable { - fileSystems."/persist/system/qbittorrent".neededForBoot = true; - - host.storage.pool.extraDatasets = { - # sops age key needs to be available to pre persist for user generation - "persist/system/qbittorrent" = { - type = "zfs_fs"; - mountpoint = "/persist/system/qbittorrent"; - options = { - canmount = "on"; - }; - }; - }; - - assertions = [ - { - assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; - message = "qbittorrent data directory does not match persistence"; - } - ]; - - environment.persistence = { - "/persist/system/root" = { - directories = [ - { - directory = qbittorent_profile_directory; - user = "qbittorrent"; - group = "qbittorrent"; - } - ]; - }; - - "/persist/system/qbittorrent" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.qbittorrent.mediaDir; - user = "qbittorrent"; - group = "qbittorrent"; - mode = "1775"; - } - ]; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix new file mode 100644 index 0000000..02d4757 --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -0,0 +1,62 @@ +{ + lib, + config, + ... +}: let + qbittorent_profile_directory = "/var/lib/qBittorrent/"; +in { + options.services.qbittorrent.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.qbittorrent.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.qbittorrent.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; + message = "qbittorrent data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.qbittorrent.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + ( + lib.mkIf config.services.qbittorrent.impermanence.enable + { + storage.impermanence.datasets = { + "persist/system/root" = { + directories."${qbittorent_profile_directory}" = { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + }; + }; + "persist/system/qbittorrent" = { + directories."${config.services.qbittorrent.mediaDir}" = { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + owner.permissions = { + read = true; + write = true; + execute = true; + }; + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = true; + write = false; + execute = true; + }; + }; + }; + }; + } + ) + ])) + ]); +} diff --git a/modules/nixos-modules/server/radarr/default.nix b/modules/nixos-modules/server/radarr/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/radarr/default.nix +++ b/modules/nixos-modules/server/radarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/radarr/impermanence.nix b/modules/nixos-modules/server/radarr/impermanence.nix deleted file mode 100644 index c948e3a..0000000 --- a/modules/nixos-modules/server/radarr/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - radarr_data_directory = "/var/lib/radarr/.config/Radarr"; -in { - options.services.radarr = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.radarr.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.radarr.impermanence.enable { - assertions = [ - { - assertion = config.services.radarr.dataDir == radarr_data_directory; - message = "radarr data directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = radarr_data_directory; - user = "radarr"; - group = "radarr"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/radarr/storage.nix b/modules/nixos-modules/server/radarr/storage.nix new file mode 100644 index 0000000..82d2bf8 --- /dev/null +++ b/modules/nixos-modules/server/radarr/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + radarr_data_directory = "/var/lib/radarr/.config/Radarr"; +in { + options.services.radarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.radarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.radarr.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.radarr.dataDir == radarr_data_directory; + message = "radarr data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.radarr.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.radarr.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${radarr_data_directory}" = { + owner.name = "radarr"; + group.name = "radarr"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/reverseProxy/default.nix b/modules/nixos-modules/server/reverseProxy/default.nix index 5d57175..336e28b 100644 --- a/modules/nixos-modules/server/reverseProxy/default.nix +++ b/modules/nixos-modules/server/reverseProxy/default.nix @@ -1,6 +1,6 @@ {...}: { imports = [ ./reverseProxy.nix - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/reverseProxy/impermanence.nix b/modules/nixos-modules/server/reverseProxy/impermanence.nix deleted file mode 100644 index 7af55df..0000000 --- a/modules/nixos-modules/server/reverseProxy/impermanence.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - lib, - config, - ... -}: let - dataDir = "/var/lib/acme"; -in { - config = lib.mkIf (config.host.impermanence.enable && config.services.reverseProxy.enable) { - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = dataDir; - user = "acme"; - group = "acme"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/reverseProxy/storage.nix b/modules/nixos-modules/server/reverseProxy/storage.nix new file mode 100644 index 0000000..c4ee04a --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/storage.nix @@ -0,0 +1,28 @@ +{ + lib, + config, + ... +}: let + dataDir = "/var/lib/acme"; +in { + options.services.reverseProxy.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.reverseProxy.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.reverseProxy.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + (lib.mkIf (!config.services.reverseProxy.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.reverseProxy.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${dataDir}" = { + owner.name = "acme"; + group.name = "acme"; + }; + }; + }) + ])) + ]); +} diff --git a/modules/nixos-modules/server/sonarr/default.nix b/modules/nixos-modules/server/sonarr/default.nix index 86dbb4b..cb2a5f0 100644 --- a/modules/nixos-modules/server/sonarr/default.nix +++ b/modules/nixos-modules/server/sonarr/default.nix @@ -1,5 +1,5 @@ {...}: { imports = [ - ./impermanence.nix + ./storage.nix ]; } diff --git a/modules/nixos-modules/server/sonarr/impermanence.nix b/modules/nixos-modules/server/sonarr/impermanence.nix deleted file mode 100644 index 5b90ee9..0000000 --- a/modules/nixos-modules/server/sonarr/impermanence.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - config, - ... -}: let - sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; -in { - options.services.sonarr = { - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.sonarr.enable && config.host.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.sonarr.impermanence.enable { - assertions = [ - { - assertion = config.services.sonarr.dataDir == sonarr_data_directory; - message = "sonarr data directory does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = sonarr_data_directory; - user = "sonarr"; - group = "sonarr"; - } - ]; - }; - }; -} diff --git a/modules/nixos-modules/server/sonarr/storage.nix b/modules/nixos-modules/server/sonarr/storage.nix new file mode 100644 index 0000000..c74a7b8 --- /dev/null +++ b/modules/nixos-modules/server/sonarr/storage.nix @@ -0,0 +1,36 @@ +{ + lib, + config, + ... +}: let + sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; +in { + options.services.sonarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.sonarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.sonarr.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.services.sonarr.dataDir == sonarr_data_directory; + message = "sonarr data directory does not match persistence"; + } + ]; + } + (lib.mkIf (!config.services.sonarr.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.sonarr.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${sonarr_data_directory}" = { + owner.name = "sonarr"; + group.name = "sonarr"; + }; + }; + }) + ])) + ]); +} From d283f881604027bb6f51d0ac8556a8cb7e394c37 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:28:34 -0600 Subject: [PATCH 11/44] feat: moved ollama, tailscale, and sync into folders following the new storage pattern --- modules/nixos-modules/ollama/default.nix | 6 ++ modules/nixos-modules/{ => ollama}/ollama.nix | 14 ---- modules/nixos-modules/ollama/storage.nix | 49 +++++++++++++ modules/nixos-modules/sync.nix | 69 ------------------- modules/nixos-modules/sync/default.nix | 6 ++ modules/nixos-modules/sync/storage.nix | 57 +++++++++++++++ modules/nixos-modules/sync/sync.nix | 36 ++++++++++ modules/nixos-modules/tailscale.nix | 34 --------- modules/nixos-modules/tailscale/default.nix | 6 ++ modules/nixos-modules/tailscale/storage.nix | 36 ++++++++++ modules/nixos-modules/tailscale/tailscale.nix | 19 +++++ 11 files changed, 215 insertions(+), 117 deletions(-) create mode 100644 modules/nixos-modules/ollama/default.nix rename modules/nixos-modules/{ => ollama}/ollama.nix (63%) create mode 100644 modules/nixos-modules/ollama/storage.nix delete mode 100644 modules/nixos-modules/sync.nix create mode 100644 modules/nixos-modules/sync/default.nix create mode 100644 modules/nixos-modules/sync/storage.nix create mode 100644 modules/nixos-modules/sync/sync.nix delete mode 100644 modules/nixos-modules/tailscale.nix create mode 100644 modules/nixos-modules/tailscale/default.nix create mode 100644 modules/nixos-modules/tailscale/storage.nix create mode 100644 modules/nixos-modules/tailscale/tailscale.nix diff --git a/modules/nixos-modules/ollama/default.nix b/modules/nixos-modules/ollama/default.nix new file mode 100644 index 0000000..896526a --- /dev/null +++ b/modules/nixos-modules/ollama/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./ollama.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/ollama.nix b/modules/nixos-modules/ollama/ollama.nix similarity index 63% rename from modules/nixos-modules/ollama.nix rename to modules/nixos-modules/ollama/ollama.nix index 99819bf..dc7cdd9 100644 --- a/modules/nixos-modules/ollama.nix +++ b/modules/nixos-modules/ollama/ollama.nix @@ -27,20 +27,6 @@ allowedUDPPorts = ports; }; })) - (lib.mkIf config.host.impermanence.enable { - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = "/var/lib/private/ollama"; - user = config.services.ollama.user; - group = config.services.ollama.group; - mode = "0700"; - } - ]; - }; - }) ] ); } diff --git a/modules/nixos-modules/ollama/storage.nix b/modules/nixos-modules/ollama/storage.nix new file mode 100644 index 0000000..ff2348e --- /dev/null +++ b/modules/nixos-modules/ollama/storage.nix @@ -0,0 +1,49 @@ +{ + config, + lib, + ... +}: { + options = { + services.ollama.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.ollama.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.ollama.enable ( + lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # Ollama needs persistent storage for models and configuration + } + (lib.mkIf (!config.services.ollama.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.ollama.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."/var/lib/private/ollama" = { + enable = true; + owner.name = config.services.ollama.user; + group.name = config.services.ollama.group; + owner.permissions = { + read = true; + write = true; + execute = false; + }; + group.permissions = { + read = false; + write = false; + execute = false; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }) + ])) + ] + ); +} diff --git a/modules/nixos-modules/sync.nix b/modules/nixos-modules/sync.nix deleted file mode 100644 index 96f54d5..0000000 --- a/modules/nixos-modules/sync.nix +++ /dev/null @@ -1,69 +0,0 @@ -{ - config, - lib, - syncthingConfiguration, - ... -}: let - mountDir = "/mnt/sync"; - configDir = "/etc/syncthing"; -in { - config = lib.mkMerge [ - { - systemd = lib.mkIf config.services.syncthing.enable { - tmpfiles.rules = [ - "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" - "d ${mountDir} 2755 syncthing syncthing -" - "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" - "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" - ]; - }; - } - (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ - { - services.syncthing = { - user = "syncthing"; - group = "syncthing"; - dataDir = "${mountDir}/default"; - configDir = configDir; - overrideDevices = true; - overrideFolders = true; - configuration = syncthingConfiguration; - deviceName = config.networking.hostName; - }; - } - - (lib.mkIf config.host.impermanence.enable { - assertions = - [ - { - assertion = config.services.syncthing.configDir == configDir; - message = "syncthing config dir does not match persistence"; - } - ] - ++ lib.attrsets.mapAttrsToList (_: folder: { - assertion = lib.strings.hasPrefix mountDir folder.path; - message = "syncthing folder ${folder.label} is stored at ${folder.path} which not under the persisted path of ${mountDir}"; - }) - config.services.syncthing.settings.folders; - environment.persistence = { - "/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = mountDir; - user = "syncthing"; - group = "syncthing"; - } - { - directory = configDir; - user = "syncthing"; - group = "syncthing"; - } - ]; - }; - }; - }) - ])) - ]; -} diff --git a/modules/nixos-modules/sync/default.nix b/modules/nixos-modules/sync/default.nix new file mode 100644 index 0000000..5640417 --- /dev/null +++ b/modules/nixos-modules/sync/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./sync.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/sync/storage.nix b/modules/nixos-modules/sync/storage.nix new file mode 100644 index 0000000..a58a49f --- /dev/null +++ b/modules/nixos-modules/sync/storage.nix @@ -0,0 +1,57 @@ +{ + config, + lib, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + options = { + services.syncthing.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.syncthing.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.syncthing.enable ( + lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # Syncthing needs persistent storage for configuration and data + } + (lib.mkIf (!config.services.syncthing.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.syncthing.impermanence.enable { + assertions = + [ + { + assertion = config.services.syncthing.configDir == configDir; + message = "syncthing config dir does not match persistence"; + } + ] + ++ lib.attrsets.mapAttrsToList (_: folder: { + assertion = lib.strings.hasPrefix mountDir folder.path; + message = "syncthing folder ${folder.label} is stored at ${folder.path} which not under the persisted path of ${mountDir}"; + }) + config.services.syncthing.settings.folders; + + storage.impermanence.datasets."persist/system/root" = { + directories = { + "${mountDir}" = { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + "${configDir}" = { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + }; + }; + }) + ])) + ] + ); +} diff --git a/modules/nixos-modules/sync/sync.nix b/modules/nixos-modules/sync/sync.nix new file mode 100644 index 0000000..28b6e38 --- /dev/null +++ b/modules/nixos-modules/sync/sync.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + syncthingConfiguration, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + config = lib.mkMerge [ + { + systemd = lib.mkIf config.services.syncthing.enable { + tmpfiles.rules = [ + "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" + "d ${mountDir} 2755 syncthing syncthing -" + "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" + "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" + ]; + }; + } + (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ + { + services.syncthing = { + user = "syncthing"; + group = "syncthing"; + dataDir = "${mountDir}/default"; + configDir = configDir; + overrideDevices = true; + overrideFolders = true; + configuration = syncthingConfiguration; + deviceName = config.networking.hostName; + }; + } + ])) + ]; +} diff --git a/modules/nixos-modules/tailscale.nix b/modules/nixos-modules/tailscale.nix deleted file mode 100644 index db664e8..0000000 --- a/modules/nixos-modules/tailscale.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - config, - lib, - ... -}: let - tailscale_data_directory = "/var/lib/tailscale"; -in { - options.host.tailscale = { - enable = lib.mkEnableOption "should tailscale be enabled on this computer"; - }; - - config = lib.mkIf config.services.tailscale.enable ( - lib.mkMerge [ - { - # any configs we want shared between all machines - } - (lib.mkIf config.host.impermanence.enable { - environment.persistence = { - "/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = tailscale_data_directory; - user = "root"; - group = "root"; - } - ]; - }; - }; - }) - ] - ); -} diff --git a/modules/nixos-modules/tailscale/default.nix b/modules/nixos-modules/tailscale/default.nix new file mode 100644 index 0000000..7a283e8 --- /dev/null +++ b/modules/nixos-modules/tailscale/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./tailscale.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/tailscale/storage.nix b/modules/nixos-modules/tailscale/storage.nix new file mode 100644 index 0000000..9533aef --- /dev/null +++ b/modules/nixos-modules/tailscale/storage.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + ... +}: let + tailscale_data_directory = "/var/lib/tailscale"; +in { + options = { + services.tailscale.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.tailscale.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.tailscale.enable ( + lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # Tailscale needs persistent storage for keys and configuration + } + (lib.mkIf (!config.services.tailscale.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.tailscale.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + directories."${tailscale_data_directory}" = { + enable = true; + owner.name = "root"; + group.name = "root"; + }; + }; + }) + ])) + ] + ); +} diff --git a/modules/nixos-modules/tailscale/tailscale.nix b/modules/nixos-modules/tailscale/tailscale.nix new file mode 100644 index 0000000..06899b1 --- /dev/null +++ b/modules/nixos-modules/tailscale/tailscale.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + ... +}: { + options = { + host.tailscale = { + enable = lib.mkEnableOption "should tailscale be enabled on this computer"; + }; + }; + + config = lib.mkIf config.services.tailscale.enable ( + lib.mkMerge [ + { + # any configs we want shared between all machines + } + ] + ); +} From ab555f50ff118a4f90720723671ad45c0c44b91e Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:30:49 -0600 Subject: [PATCH 12/44] fix: defiant config cache drive converted to correct format --- configurations/nixos/defiant/configuration.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 11a6f9d..182b8c0 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -100,12 +100,12 @@ ] ]; # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA - cache = { - cache0 = { + cache = [ + { device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"; boot = true; - }; - }; + } + ]; }; }; impermanence = { From 703530ddfe22c61f475a1f9a3ada43264993fa2c Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:48:41 -0600 Subject: [PATCH 13/44] feat: updated storage config for emergent --- configurations/nixos/emergent/configuration.nix | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/configurations/nixos/emergent/configuration.nix b/configurations/nixos/emergent/configuration.nix index 6121069..781d66b 100644 --- a/configurations/nixos/emergent/configuration.nix +++ b/configurations/nixos/emergent/configuration.nix @@ -59,12 +59,22 @@ hardware = { piperMouse.enable = true; }; + }; - storage = { + storage = { + zfs = { enable = true; pool = { - mode = ""; - drives = ["wwn-0x5000039fd0cf05eb"]; + mode = "stripe"; + vdevs = [ + [ + { + device = "wwn-0x5000039fd0cf05eb"; + boot = true; + } + ] + ]; + cache = []; }; }; }; From 5acf060e9e9e874acfe7cdecd75e9ff67afa53b5 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:49:19 -0600 Subject: [PATCH 14/44] feat: updated imports to use new storage module only --- modules/nixos-modules/default.nix | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/nixos-modules/default.nix b/modules/nixos-modules/default.nix index 77bfe93..34e041e 100644 --- a/modules/nixos-modules/default.nix +++ b/modules/nixos-modules/default.nix @@ -8,12 +8,10 @@ ./desktop.nix ./ssh.nix ./i18n.nix - ./sync.nix - ./impermanence.nix - ./disko.nix - ./ollama.nix + ./sync + ./ollama ./ai.nix - ./tailscale.nix + ./tailscale ./steam.nix ./server ./storage From 1310b50794cc8261ea129695814f270ce8df056b Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 19:04:59 -0600 Subject: [PATCH 15/44] feat: moved ssh config to use new storage config --- modules/nixos-modules/ssh.nix | 41 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix index 6f5fac1..20e7881 100644 --- a/modules/nixos-modules/ssh.nix +++ b/modules/nixos-modules/ssh.nix @@ -3,6 +3,13 @@ config, ... }: { + options = { + services.openssh.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.openssh.enable && config.storage.impermanence.enable; + }; + }; + config = lib.mkMerge [ { services = { @@ -17,12 +24,32 @@ }; }; } - (lib.mkIf config.host.impermanence.enable { - environment.persistence."/persist/system/root" = { - files = lib.lists.flatten ( - builtins.map (hostKey: [hostKey.path "${hostKey.path}.pub"]) config.services.openssh.hostKeys - ); - }; - }) + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # SSH host keys need to be persisted to maintain server identity + } + (lib.mkIf (!config.services.openssh.impermanence.enable) { + # TODO: placeholder to configure a unique dataset for this service + }) + (lib.mkIf config.services.openssh.impermanence.enable { + storage.impermanence.datasets."persist/system/root" = { + files = builtins.listToAttrs ( + lib.lists.flatten ( + builtins.map (hostKey: [ + { + name = hostKey.path; + value = {enable = true;}; + } + { + name = "${hostKey.path}.pub"; + value = {enable = true;}; + } + ]) + config.services.openssh.hostKeys + ) + ); + }; + }) + ])) ]; } From 4da5d65d8f81e4add37ed0ddda014515e324f29f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 21:10:18 -0600 Subject: [PATCH 16/44] feat: added activation and resume scripts to storage and impermanence --- .../nixos-modules/storage/impermanence.nix | 17 +++++++++-- modules/nixos-modules/storage/storage.nix | 29 +++++++++++-------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 6619bc5..33b4706 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -66,6 +66,20 @@ in { } ]; + # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 + system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; + system.activationScripts = { + "var-lib-private-permissions" = { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/system/root/var/lib/private + chmod 0700 /persist/system/root/var/lib/private + ''; + }; + }; + + programs.fuse.userAllowOther = true; + environment.persistence = lib.mapAttrs (datasetName: dataset: { enable = true; @@ -90,9 +104,6 @@ in { # TODO: need for boot on filesystems } (lib.mkIf config.storage.zfs.enable { - # TODO: activationScripts config for private folders - # TODO: rollback post resume - # TODO: fuse userAllowOther storage.zfs.datasets = lib.mapAttrs ( datasetName: dataset: diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index b6428f6..d6a2a2b 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -50,6 +50,23 @@ }; }) (lib.mkIf config.storage.impermanence.enable { + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/local/system/root@blank + ''; + + storage.zfs.datasets = { + "local/system/root" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/"; + }; + snapshot = { + blankSnapshot = true; + }; + }; + }; + storage.impermanence.datasets = { "persist/system/root" = { mount = { @@ -65,18 +82,6 @@ }; }; }; - storage.zfs.datasets = { - "local/system/root" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/"; - }; - snapshot = { - blankSnapshot = true; - }; - }; - }; # TODO: home-manager.users..storage.impermanence.enable # is false then persist the entire directory of the user From 4d7d11e0c886ebb0a7e252f30eb57cf6fdc9b7f1 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 21:19:54 -0600 Subject: [PATCH 17/44] feat: removed now unneeded disko and impermanence modules --- modules/nixos-modules/disko.nix | 267 ------------------------- modules/nixos-modules/impermanence.nix | 101 ---------- 2 files changed, 368 deletions(-) delete mode 100644 modules/nixos-modules/disko.nix delete mode 100644 modules/nixos-modules/impermanence.nix diff --git a/modules/nixos-modules/disko.nix b/modules/nixos-modules/disko.nix deleted file mode 100644 index a962689..0000000 --- a/modules/nixos-modules/disko.nix +++ /dev/null @@ -1,267 +0,0 @@ -{ - lib, - pkgs, - config, - inputs, - ... -}: let - # there currently is a bug with disko that causes long disk names to be generated improperly this hash function should alleviate it when used for disk names instead of what we are defaulting to - # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that - hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); - - vdevs = - builtins.map ( - disks: - builtins.map (disk: lib.attrsets.nameValuePair (hashDisk disk) disk) disks - ) - config.host.storage.pool.vdevs; - cache = - builtins.map ( - disk: lib.attrsets.nameValuePair (hashDisk disk) disk - ) - config.host.storage.pool.cache; - - datasets = config.host.storage.pool.datasets // config.host.storage.pool.extraDatasets; -in { - options.host.storage = { - enable = lib.mkEnableOption "are we going create zfs disks with disko on this device"; - encryption = lib.mkEnableOption "is the vdev going to be encrypted"; - notifications = { - enable = lib.mkEnableOption "are notifications enabled"; - host = lib.mkOption { - type = lib.types.str; - description = "what is the host that we are going to send the email to"; - }; - port = lib.mkOption { - type = lib.types.port; - description = "what port is the host using to receive mail on"; - }; - to = lib.mkOption { - type = lib.types.str; - description = "what account is the email going to be sent to"; - }; - user = lib.mkOption { - type = lib.types.str; - description = "what user is the email going to be set from"; - }; - tokenFile = lib.mkOption { - type = lib.types.str; - description = "file containing the password to be used by msmtp for notifications"; - }; - }; - pool = { - mode = lib.mkOption { - type = lib.types.str; - default = "raidz2"; - description = "what level of redundancy should this pool have"; - }; - # list of drives in pool that will have a boot partition put onto them - bootDrives = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "list of disks that are going to have a boot partition installed on them"; - default = lib.lists.flatten config.host.storage.pool.vdevs; - }; - # shorthand for vdevs if you only have 1 vdev - drives = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "list of drives that are going to be in the vdev"; - default = []; - }; - # list of all drives in each vdev - vdevs = lib.mkOption { - type = lib.types.listOf (lib.types.listOf lib.types.str); - description = "list of disks that are going to be in"; - default = [config.host.storage.pool.drives]; - }; - # list of cache drives for pool - cache = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "list of drives that are going to be used as cache"; - default = []; - }; - # Default datasets that are needed to make a functioning system - datasets = lib.mkOption { - type = lib.types.attrsOf (inputs.disko.lib.subType { - types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; - }); - default = { - "local" = { - type = "zfs_fs"; - options.canmount = "off"; - }; - # nix directory needs to be available pre persist and doesn't need to be snapshotted or backed up - "local/system/nix" = { - type = "zfs_fs"; - mountpoint = "/nix"; - options = { - atime = "off"; - relatime = "off"; - canmount = "on"; - }; - }; - # dataset for root that gets rolled back on every boot - "local/system/root" = { - type = "zfs_fs"; - mountpoint = "/"; - options = { - canmount = "on"; - }; - postCreateHook = '' - zfs snapshot rpool/local/system/root@blank - ''; - }; - }; - }; - extraDatasets = lib.mkOption { - type = lib.types.attrsOf (inputs.disko.lib.subType { - types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; - }); - description = "List of datasets to define"; - default = {}; - }; - }; - }; - - config = lib.mkIf config.host.storage.enable { - programs.msmtp = lib.mkIf config.host.storage.notifications.enable { - enable = true; - setSendmail = true; - defaults = { - aliases = "/etc/aliases"; - port = config.host.storage.notifications.port; - tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; - tls = "on"; - auth = "login"; - tls_starttls = "off"; - }; - accounts = { - zfs_notifications = { - auth = true; - tls = true; - host = config.host.storage.notifications.host; - passwordeval = "cat ${config.host.storage.notifications.tokenFile}"; - user = config.host.storage.notifications.user; - from = config.host.storage.notifications.user; - }; - }; - }; - - services.zfs = { - autoScrub.enable = true; - autoSnapshot.enable = true; - - zed = lib.mkIf config.host.storage.notifications.enable { - enableMail = true; - - settings = { - ZED_DEBUG_LOG = "/tmp/zed.debug.log"; - ZED_EMAIL_ADDR = [config.host.storage.notifications.to]; - ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; - ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; - - ZED_NOTIFY_INTERVAL_SECS = 3600; - ZED_NOTIFY_VERBOSE = true; - - ZED_USE_ENCLOSURE_LEDS = true; - ZED_SCRUB_AFTER_RESILVER = true; - }; - }; - }; - - disko.devices = { - disk = ( - builtins.listToAttrs ( - builtins.map - (drive: - lib.attrsets.nameValuePair (drive.name) { - type = "disk"; - device = "/dev/disk/by-id/${drive.value}"; - content = { - type = "gpt"; - partitions = { - ESP = lib.mkIf (builtins.elem drive.value config.host.storage.pool.bootDrives) { - # The 2GB here for the boot partition might be a bit overkill we probably only need like 1/4th of that but storage is cheap - size = "2G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = ["umask=0077"]; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "rpool"; - }; - }; - }; - }; - }) - ( - (lib.lists.flatten vdevs) ++ cache - ) - ) - ); - zpool = { - rpool = { - type = "zpool"; - mode = { - topology = { - type = "topology"; - vdev = ( - builtins.map (disks: { - mode = config.host.storage.pool.mode; - members = - builtins.map (disk: disk.name) disks; - }) - vdevs - ); - cache = builtins.map (disk: disk.name) cache; - }; - }; - - options = { - ashift = "12"; - autotrim = "on"; - }; - - rootFsOptions = - { - canmount = "off"; - mountpoint = "none"; - - xattr = "sa"; - acltype = "posixacl"; - relatime = "on"; - - compression = "lz4"; - - "com.sun:auto-snapshot" = "false"; - } - // ( - lib.attrsets.optionalAttrs config.host.storage.encryption { - encryption = "on"; - keyformat = "hex"; - keylocation = "prompt"; - } - ); - - datasets = lib.mkMerge [ - ( - lib.attrsets.mapAttrs (name: value: { - type = value.type; - options = value.options; - mountpoint = value.mountpoint; - postCreateHook = value.postCreateHook; - }) - datasets - ) - ]; - }; - }; - }; - }; -} diff --git a/modules/nixos-modules/impermanence.nix b/modules/nixos-modules/impermanence.nix deleted file mode 100644 index 4cdcd00..0000000 --- a/modules/nixos-modules/impermanence.nix +++ /dev/null @@ -1,101 +0,0 @@ -{ - config, - lib, - ... -}: { - options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device"; - - config = lib.mkMerge [ - { - assertions = [ - { - assertion = !(config.host.impermanence.enable && !config.host.storage.enable); - message = '' - Disko storage must be enabled to use impermanence. - ''; - } - ]; - } - ( - lib.mkIf config.host.impermanence.enable { - assertions = [ - { - assertion = config.host.impermanence.enable && config.host.storage.enable; - message = "Impermanence can not be used without managed host storage."; - } - ]; - - # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 - system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; - system.activationScripts = { - "var-lib-private-permissions" = { - deps = ["specialfs"]; - text = '' - mkdir -p /persist/system/root/var/lib/private - chmod 0700 /persist/system/root/var/lib/private - ''; - }; - }; - - programs.fuse.userAllowOther = true; - - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/local/system/root@blank - ''; - - fileSystems = { - "/".neededForBoot = true; - "/persist/system/root".neededForBoot = true; - "/persist/system/var/log".neededForBoot = true; - }; - - host.storage.pool.extraDatasets = { - # persist datasets are datasets that contain information that we would like to keep around - "persist" = { - type = "zfs_fs"; - options.canmount = "off"; - options = { - "com.sun:auto-snapshot" = "true"; - }; - }; - # this is where root data actually lives - "persist/system/root" = { - type = "zfs_fs"; - mountpoint = "/persist/system/root"; - }; - "persist/system/var/log" = { - type = "zfs_fs"; - mountpoint = "/persist/system/var/log"; - # logs should be append only so we shouldn't need to snapshot them - options = { - "com.sun:auto-snapshot" = "false"; - }; - }; - }; - - environment.persistence."/persist/system/var/log" = { - enable = true; - hideMounts = true; - directories = [ - "/var/log" - ]; - }; - - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - "/var/lib/nixos" - "/var/lib/systemd/coredump" - ]; - files = [ - "/etc/machine-id" - ]; - }; - - # TODO: this should live in leylas home manager configuration - security.sudo.extraConfig = "Defaults lecture=never"; - } - ) - ]; -} From 318a0a974884ce803d7bd5517c56692b822f9a56 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 22:37:19 -0600 Subject: [PATCH 18/44] feat: added sops dataset to users.nix --- modules/nixos-modules/users.nix | 138 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 71 deletions(-) diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 987e080..3385a83 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -399,79 +399,75 @@ in { }; }; } - (lib.mkIf config.host.impermanence.enable { - boot.initrd.postResumeCommands = lib.mkAfter ( - lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") - normalUsers) - ); + (lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ + (lib.mkIf config.storage.zfs.enable { + storage.zfs.datasets."persist/system/sops" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = SOPS_AGE_KEY_DIRECTORY; + }; + atime = "off"; + relatime = "off"; + }; + }) + ])) + # (lib.mkIf config.host.impermanence.enable { + # boot.initrd.postResumeCommands = lib.mkAfter ( + # lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") + # normalUsers) + # ); - systemd = { - tmpfiles.rules = - builtins.map ( - user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" - ) - normalUsers; - }; + # systemd = { + # tmpfiles.rules = + # builtins.map ( + # user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" + # ) + # normalUsers; + # }; - fileSystems = lib.mkMerge [ - { - ${SOPS_AGE_KEY_DIRECTORY}.neededForBoot = true; - } - ( - builtins.listToAttrs ( - builtins.map (user: - lib.attrsets.nameValuePair "/persist/home/${user.name}" { - neededForBoot = true; - }) - normalUsers - ) - ) - ( - builtins.listToAttrs ( - builtins.map (user: - lib.attrsets.nameValuePair "/home/${user.name}" { - neededForBoot = true; - }) - normalUsers - ) - ) - ]; + # fileSystems = lib.mkMerge [ + # ( + # builtins.listToAttrs ( + # builtins.map (user: + # lib.attrsets.nameValuePair "/persist/home/${user.name}" { + # neededForBoot = true; + # }) + # normalUsers + # ) + # ) + # ( + # builtins.listToAttrs ( + # builtins.map (user: + # lib.attrsets.nameValuePair "/home/${user.name}" { + # neededForBoot = true; + # }) + # normalUsers + # ) + # ) + # ]; - host.storage.pool.extraDatasets = lib.mkMerge ( - [ - { - # sops age key needs to be available to pre persist for user generation - "local/system/sops" = { - type = "zfs_fs"; - mountpoint = SOPS_AGE_KEY_DIRECTORY; - options = { - atime = "off"; - relatime = "off"; - canmount = "on"; - }; - }; - } - ] - ++ ( - builtins.map (user: { - "local/home/${user.name}" = { - type = "zfs_fs"; - mountpoint = "/home/${user.name}"; - options = { - canmount = "on"; - }; - postCreateHook = '' - zfs snapshot rpool/local/home/${user.name}@blank - ''; - }; - "persist/home/${user.name}" = { - type = "zfs_fs"; - mountpoint = "/persist/home/${user.name}"; - }; - }) - normalUsers - ) - ); - }) + # host.storage.pool.extraDatasets = lib.mkMerge ( + # ( + # builtins.map (user: { + # "local/home/${user.name}" = { + # type = "zfs_fs"; + # mountpoint = "/home/${user.name}"; + # options = { + # canmount = "on"; + # }; + # postCreateHook = '' + # zfs snapshot rpool/local/home/${user.name}@blank + # ''; + # }; + # "persist/home/${user.name}" = { + # type = "zfs_fs"; + # mountpoint = "/persist/home/${user.name}"; + # }; + # }) + # normalUsers + # ) + # ); + # }) ]; } From d06c25f33f07e198d00da6331ed26cf470deb9ff Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 02:38:28 -0600 Subject: [PATCH 19/44] feat: migrated users over to new persistence structure --- configurations/home-manager/leyla/default.nix | 2 +- .../home-manager/leyla/impermanence.nix | 1 - modules/home-manager-modules/impermanence.nix | 5 +- .../home-manager-modules/programs/anki.nix | 4 +- .../programs/bitwarden.nix | 1 - .../home-manager-modules/programs/bruno.nix | 1 - .../home-manager-modules/programs/calibre.nix | 1 - .../programs/davinci-resolve.nix | 1 - .../home-manager-modules/programs/dbeaver.nix | 1 - .../home-manager-modules/programs/discord.nix | 1 - .../home-manager-modules/programs/firefox.nix | 1 - .../home-manager-modules/programs/freecad.nix | 1 - .../home-manager-modules/programs/gimp.nix | 1 - .../programs/inkscape.nix | 1 - .../programs/kdenlive.nix | 1 - .../home-manager-modules/programs/krita.nix | 1 - .../programs/libreoffice.nix | 1 - .../programs/mapillary-uploader.nix | 1 - modules/home-manager-modules/programs/obs.nix | 1 - .../home-manager-modules/programs/olympus.nix | 1 - .../home-manager-modules/programs/openrgb.nix | 1 - .../home-manager-modules/programs/picard.nix | 1 - .../programs/qflipper.nix | 1 - .../home-manager-modules/programs/steam.nix | 1 - .../programs/tor-browser.nix | 1 - .../programs/ungoogled-chromium.nix | 1 - modules/home-manager-modules/programs/via.nix | 1 - .../programs/vmware-workstation.nix | 1 - modules/nixos-modules/users.nix | 120 ++++++++---------- 29 files changed, 57 insertions(+), 99 deletions(-) diff --git a/configurations/home-manager/leyla/default.nix b/configurations/home-manager/leyla/default.nix index 8a37754..20b04c7 100644 --- a/configurations/home-manager/leyla/default.nix +++ b/configurations/home-manager/leyla/default.nix @@ -12,7 +12,7 @@ ]; config = { - impermanence.enable = osConfig.host.impermanence.enable; + impermanence.enable = osConfig.storage.impermanence.enable; # Home Manager needs a bit of information about you and the paths it should # manage. diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index ce81c81..ea64d56 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -14,7 +14,6 @@ ".bash_history" # keep shell history around "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files ]; - allowOther = true; }; }; } diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index 6c75edd..402cccd 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -18,17 +18,16 @@ in { (lib.mkIf config.impermanence.enable { assertions = [ { - assertion = osConfig.host.impermanence.enable; + assertion = osConfig.storage.impermanence.enable; message = "impermanence can not be enabled for a user when it is not enabled for the system"; } ]; }) # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback - (lib.mkIf (osConfig.host.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { + (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { home.persistence."/persist/home/${config.home.username}" = { directories = ["."]; - allowOther = true; }; }) ]; diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix index c2f93ea..2e3f3fc 100644 --- a/modules/home-manager-modules/programs/anki.nix +++ b/modules/home-manager-modules/programs/anki.nix @@ -1,15 +1,13 @@ { lib, config, - osConfig, ... }: { - config = lib.mkIf (config.programs.anki.enable && osConfig.host.impermanence.enable) { + config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/Anki2/" ]; - allowOther = true; }; }; } diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index e305b6c..040d875 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/Bitwarden" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index 8ad5e63..871cca0 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/bruno/" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index dbe6e2b..9219f31 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/calibre" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix index 6c4526f..c5fed5a 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -21,7 +21,6 @@ "${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.configHome}/blackmagic" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index 8b6c41a..87786a7 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.dataHome}/DBeaverData/" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index d5d7192..cc06bca 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/discord/" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix index 8841887..e50217a 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -22,7 +22,6 @@ # Extension configuration ".mozilla/firefox/${profile}/extension-settings.json" ]; - allowOther = true; }; in { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index 89668de..553de9e 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/FreeCAD" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index 925a2d9..6ec4a6f 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/GIMP" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index a26ddec..b5f5dbf 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/inkscape" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix index 05327d1..6773b19 100644 --- a/modules/home-manager-modules/programs/kdenlive.nix +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -28,7 +28,6 @@ in { "${config.xdg.configHome}/kdenliverc" "${config.xdg.dataHome}/kdenlive" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index 3ba5560..bbf9416 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -21,7 +21,6 @@ "${config.xdg.configHome}/kritarc" "${config.xdg.dataHome}/krita" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index 93163e7..618acc3 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/libreoffice" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index df1f093..f5cbb0e 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -22,7 +22,6 @@ in { "${config.xdg.configHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index bfdba90..84d49b1 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -10,7 +10,6 @@ directories = [ "${config.xdg.configHome}/obs-studio" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix index 0e38eec..b3cfd21 100644 --- a/modules/home-manager-modules/programs/olympus.nix +++ b/modules/home-manager-modules/programs/olympus.nix @@ -28,7 +28,6 @@ in { "${config.xdg.configHome}/olympus" "${config.xdg.dataHome}/olympus" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index c9d5e14..2372f54 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/OpenRGB" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index bc37b86..b61dd8c 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/MusicBrainz" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index 8b42766..6963acb 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/qFlipper" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index fd98cb6..6262eac 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -25,7 +25,6 @@ method = "symlink"; } ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index c3b085d..bc7eddc 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.dataHome}/torbrowser" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix index ef6a881..8b0ade8 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -20,7 +20,6 @@ directories = [ "${config.xdg.configHome}/chromium" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index 0aa58e4..524576d 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -21,7 +21,6 @@ "${config.xdg.configHome}/via" "${config.xdg.dataHome}/via" ]; - allowOther = true; }; } ) diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix index 8e9d406..f6a3ce1 100644 --- a/modules/home-manager-modules/programs/vmware-workstation.nix +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -28,7 +28,6 @@ method = "symlink"; } ]; - allowOther = true; }; } ) diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 3385a83..040261a 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -400,74 +400,60 @@ in { }; } (lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable { - storage.zfs.datasets."persist/system/sops" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = SOPS_AGE_KEY_DIRECTORY; + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # sops age key needs to be available to pre persist for user generation + storage.zfs.datasets = lib.mkMerge [ + { + "local/system/sops" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = SOPS_AGE_KEY_DIRECTORY; + }; + atime = "off"; + relatime = "off"; + }; + } + # Create ZFS datasets for each normal user + (lib.mkMerge ( + builtins.map (user: { + "local/home/${user.name}" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/home/${user.name}"; + }; + snapshot.blankSnapshot = true; + }; + "persist/home/${user.name}" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/persist/home/${user.name}"; + }; + }; + }) + normalUsers + )) + ]; + + # Post resume commands to rollback user home datasets to blank snapshots + boot.initrd.postResumeCommands = lib.mkAfter ( + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") + normalUsers) + ); + + # Create persist home directories with proper permissions + systemd = { + tmpfiles.rules = + builtins.map ( + user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" + ) + normalUsers; }; - atime = "off"; - relatime = "off"; - }; - }) + } + ])) ])) - # (lib.mkIf config.host.impermanence.enable { - # boot.initrd.postResumeCommands = lib.mkAfter ( - # lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") - # normalUsers) - # ); - - # systemd = { - # tmpfiles.rules = - # builtins.map ( - # user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" - # ) - # normalUsers; - # }; - - # fileSystems = lib.mkMerge [ - # ( - # builtins.listToAttrs ( - # builtins.map (user: - # lib.attrsets.nameValuePair "/persist/home/${user.name}" { - # neededForBoot = true; - # }) - # normalUsers - # ) - # ) - # ( - # builtins.listToAttrs ( - # builtins.map (user: - # lib.attrsets.nameValuePair "/home/${user.name}" { - # neededForBoot = true; - # }) - # normalUsers - # ) - # ) - # ]; - - # host.storage.pool.extraDatasets = lib.mkMerge ( - # ( - # builtins.map (user: { - # "local/home/${user.name}" = { - # type = "zfs_fs"; - # mountpoint = "/home/${user.name}"; - # options = { - # canmount = "on"; - # }; - # postCreateHook = '' - # zfs snapshot rpool/local/home/${user.name}@blank - # ''; - # }; - # "persist/home/${user.name}" = { - # type = "zfs_fs"; - # mountpoint = "/persist/home/${user.name}"; - # }; - # }) - # normalUsers - # ) - # ); - # }) ]; } From 61eef3067e36c187b4615b2ad9d1d7101fe4027f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:42:25 -0600 Subject: [PATCH 20/44] feat: made persist build with new impermanence system --- configurations/home-manager/leyla/impermanence.nix | 2 +- modules/home-manager-modules/impermanence.nix | 2 +- modules/home-manager-modules/openssh.nix | 2 +- modules/home-manager-modules/programs/anki.nix | 4 ++-- modules/home-manager-modules/programs/bitwarden.nix | 2 +- modules/home-manager-modules/programs/bruno.nix | 2 +- modules/home-manager-modules/programs/calibre.nix | 2 +- modules/home-manager-modules/programs/davinci-resolve.nix | 2 +- modules/home-manager-modules/programs/dbeaver.nix | 2 +- modules/home-manager-modules/programs/discord.nix | 2 +- modules/home-manager-modules/programs/firefox.nix | 2 +- modules/home-manager-modules/programs/freecad.nix | 2 +- modules/home-manager-modules/programs/gimp.nix | 2 +- modules/home-manager-modules/programs/idea.nix | 2 +- modules/home-manager-modules/programs/inkscape.nix | 2 +- modules/home-manager-modules/programs/kdenlive.nix | 2 +- modules/home-manager-modules/programs/krita.nix | 2 +- modules/home-manager-modules/programs/libreoffice.nix | 2 +- modules/home-manager-modules/programs/makemkv.nix | 2 +- modules/home-manager-modules/programs/mapillary-uploader.nix | 2 +- modules/home-manager-modules/programs/obs.nix | 2 +- modules/home-manager-modules/programs/obsidian.nix | 2 +- modules/home-manager-modules/programs/olympus.nix | 2 +- modules/home-manager-modules/programs/openrgb.nix | 2 +- modules/home-manager-modules/programs/picard.nix | 2 +- modules/home-manager-modules/programs/prostudiomasters.nix | 2 +- modules/home-manager-modules/programs/protonvpn.nix | 2 +- modules/home-manager-modules/programs/qbittorrent.nix | 2 +- modules/home-manager-modules/programs/qflipper.nix | 2 +- modules/home-manager-modules/programs/signal.nix | 2 +- modules/home-manager-modules/programs/steam.nix | 2 +- modules/home-manager-modules/programs/tor-browser.nix | 2 +- modules/home-manager-modules/programs/ungoogled-chromium.nix | 2 +- modules/home-manager-modules/programs/via.nix | 2 +- modules/home-manager-modules/programs/vmware-workstation.nix | 2 +- 35 files changed, 36 insertions(+), 36 deletions(-) diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index ea64d56..c61d693 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.impermanence.enable) { - home.persistence."/persist/home/leyla" = { + home.persistence."/persist/home" = { directories = [ "desktop" "downloads" diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index 402cccd..67f0ee4 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -26,7 +26,7 @@ in { # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."/persist/home/${config.home.username}" = { + home.persistence."/persist/home" = { directories = ["."]; }; }) diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix index afc98dd..213ad67 100644 --- a/modules/home-manager-modules/openssh.nix +++ b/modules/home-manager-modules/openssh.nix @@ -96,7 +96,7 @@ } ) (lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { files = lib.lists.flatten ( builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys ); diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix index 2e3f3fc..739245e 100644 --- a/modules/home-manager-modules/programs/anki.nix +++ b/modules/home-manager-modules/programs/anki.nix @@ -4,9 +4,9 @@ ... }: { config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ - "${config.xdg.dataHome}/Anki2/" + ".local/share/Anki2" ]; }; }; diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index 040d875..c752669 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/Bitwarden" ]; diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index 871cca0..768299b 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/bruno/" ]; diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index 9219f31..14e48dc 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/calibre" ]; diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix index c5fed5a..49c7c47 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.configHome}/blackmagic" diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index 87786a7..abc7c29 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.dataHome}/DBeaverData/" ]; diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index cc06bca..c62de57 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/discord/" ]; diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix index e50217a..282c022 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -25,7 +25,7 @@ }; in { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { - home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge ( + home.persistence."/persist/home" = lib.mkMerge ( ( lib.attrsets.mapAttrsToList (profile: _: buildProfilePersistence profile) diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index 553de9e..c546794 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/FreeCAD" ]; diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index 6ec4a6f..c127234 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/GIMP" ]; diff --git a/modules/home-manager-modules/programs/idea.nix b/modules/home-manager-modules/programs/idea.nix index e59e7b2..438e345 100644 --- a/modules/home-manager-modules/programs/idea.nix +++ b/modules/home-manager-modules/programs/idea.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ # configuration "${config.xdg.configHome}/JetBrains/" diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index b5f5dbf..8cef5cb 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/inkscape" ]; diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix index 6773b19..a130fb8 100644 --- a/modules/home-manager-modules/programs/kdenlive.nix +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/kdenliverc" "${config.xdg.dataHome}/kdenlive" diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index bbf9416..869b10b 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/kritarc" "${config.xdg.dataHome}/krita" diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index 618acc3..924d2a1 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/libreoffice" ]; diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix index e92c3d3..e158c07 100644 --- a/modules/home-manager-modules/programs/makemkv.nix +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -30,7 +30,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ ".MakeMKV" ]; diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index f5cbb0e..7fb416e 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -17,7 +17,7 @@ in { } ( mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader" diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index 84d49b1..5e226cc 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/obs-studio" ]; diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix index 824563d..91b59b3 100644 --- a/modules/home-manager-modules/programs/obsidian.nix +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/obsidian" ]; diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix index b3cfd21..2be0084 100644 --- a/modules/home-manager-modules/programs/olympus.nix +++ b/modules/home-manager-modules/programs/olympus.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/olympus" "${config.xdg.dataHome}/olympus" diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index 2372f54..94636fc 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/OpenRGB" ]; diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index b61dd8c..a6a7887 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/MusicBrainz" ]; diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix index 5345169..3653ae4 100644 --- a/modules/home-manager-modules/programs/prostudiomasters.nix +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/ProStudioMasters" ]; diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix index 513a610..d04c012 100644 --- a/modules/home-manager-modules/programs/protonvpn.nix +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/protonvpn" "${config.xdg.configHome}/Proton" diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix index 61d13c0..37fd464 100644 --- a/modules/home-manager-modules/programs/qbittorrent.nix +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/qBittorrent" ]; diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index 6963acb..8261f5e 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/qFlipper" ]; diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index 7db23a7..3dae867 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/Signal" ]; diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index 6262eac..98b970f 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -18,7 +18,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ { directory = "${config.xdg.dataHome}/Steam"; diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index bc7eddc..e13dd4c 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.dataHome}/torbrowser" ]; diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix index 8b0ade8..8ca8ec0 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/chromium" ]; diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index 524576d..acf2d8c 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ "${config.xdg.configHome}/via" "${config.xdg.dataHome}/via" diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix index f6a3ce1..30ae692 100644 --- a/modules/home-manager-modules/programs/vmware-workstation.nix +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -17,7 +17,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist${config.home.homeDirectory}" = { + home.persistence."/persist/home" = { directories = [ { directory = ".vmware"; From 8aa984a389b949ca4e0fad20c32af931341b2083 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:49:12 -0600 Subject: [PATCH 21/44] feat: made datasets build --- .../nixos-modules/storage/impermanence.nix | 10 +++++- modules/nixos-modules/storage/storage.nix | 34 ++++++++++++------- .../submodules/impermanenceDataset.nix | 4 +-- modules/nixos-modules/users.nix | 8 ----- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 33b4706..4f231bf 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -80,6 +80,15 @@ in { programs.fuse.userAllowOther = true; + fileSystems = + lib.mapAttrs' ( + datasetName: dataset: + lib.nameValuePair "/${datasetName}" { + neededForBoot = true; + } + ) + config.storage.impermanence.datasets; + environment.persistence = lib.mapAttrs (datasetName: dataset: { enable = true; @@ -101,7 +110,6 @@ in { }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); }) config.storage.impermanence.datasets; - # TODO: need for boot on filesystems } (lib.mkIf config.storage.zfs.enable { storage.zfs.datasets = diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index d6a2a2b..e9f740b 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -35,17 +35,19 @@ }; } (lib.mkIf (!config.storage.impermanence.enable) { - # TODO: create datasets for systemd.services..storage.impermanence.datasets - storage.zfs.datasets = { - "persist/system/root" = { - type = "zfs_fs"; - mount = { - enable = false; - mountPoint = "/"; - }; - snapshot = { - autoSnapshot = true; - }; + storage.zfs.rootDataset = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/"; + }; + compression = "lz4"; + acltype = "posixacl"; + relatime = "on"; + xattr = "sa"; + snapshot = { + autoSnapshot = true; + blankSnapshot = true; }; }; }) @@ -70,8 +72,8 @@ storage.impermanence.datasets = { "persist/system/root" = { mount = { - enable = false; - mountPoint = "/"; + enable = true; + mountPoint = "/persist/system/root"; }; directories = { "/var/lib/nixos".enable = true; @@ -81,6 +83,12 @@ "/etc/machine-id".enable = true; }; }; + "persist/home" = { + mount = { + enable = true; + mountPoint = "/persist/home"; + }; + }; }; # TODO: home-manager.users..storage.impermanence.enable diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 7154e90..0104b88 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -14,14 +14,14 @@ args @ {lib, ...}: {name, ...}: let owner = { name = lib.mkOption { type = lib.types.str; - default = "nouser"; + default = "root"; }; permissions = pathPermissions; }; group = { name = lib.mkOption { type = lib.types.str; - default = "nogroup"; + default = "root"; }; permissions = pathPermissions; }; diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 040261a..bf45ac9 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -415,7 +415,6 @@ in { relatime = "off"; }; } - # Create ZFS datasets for each normal user (lib.mkMerge ( builtins.map (user: { "local/home/${user.name}" = { @@ -426,13 +425,6 @@ in { }; snapshot.blankSnapshot = true; }; - "persist/home/${user.name}" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/persist/home/${user.name}"; - }; - }; }) normalUsers )) From ac0f1ce2e69c3da050f44eb1ed93e54b213de353 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:51:28 -0600 Subject: [PATCH 22/44] feat: updated flake input to use fork --- flake.lock | 19 ++++++++++++++----- flake.nix | 4 +++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 3c1f2ad..6b33266 100644 --- a/flake.lock +++ b/flake.lock @@ -147,16 +147,25 @@ } }, "impermanence": { + "inputs": { + "home-manager": [ + "home-manager" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1737831083, - "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", - "owner": "nix-community", + "lastModified": 1762761176, + "narHash": "sha256-i3gM8fUozQrgZIbwVNlTuhLqPSl56zxAYpsQpQ9Lhro=", + "owner": "jan-leila", "repo": "impermanence", - "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "rev": "ffbe1ca47cf4b3008c3aa5c49cdae294d8c8058a", "type": "github" }, "original": { - "owner": "nix-community", + "owner": "jan-leila", + "ref": "home-manager-v2", "repo": "impermanence", "type": "github" } diff --git a/flake.nix b/flake.nix index 6f85fa3..f0b9d67 100644 --- a/flake.nix +++ b/flake.nix @@ -36,7 +36,9 @@ # delete your darlings impermanence = { - url = "github:nix-community/impermanence"; + url = "github:jan-leila/impermanence/home-manager-v2"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; }; nix-darwin = { From 1eb66d1c31e5dbd8471aa2652b0db11115389444 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Wed, 12 Nov 2025 19:27:12 -0600 Subject: [PATCH 23/44] feat: updated pool names --- .../home-manager/leyla/impermanence.nix | 2 +- modules/home-manager-modules/impermanence.nix | 2 +- modules/home-manager-modules/openssh.nix | 2 +- .../home-manager-modules/programs/anki.nix | 2 +- .../programs/bitwarden.nix | 2 +- .../home-manager-modules/programs/bruno.nix | 2 +- .../home-manager-modules/programs/calibre.nix | 2 +- .../programs/davinci-resolve.nix | 2 +- .../home-manager-modules/programs/dbeaver.nix | 2 +- .../home-manager-modules/programs/discord.nix | 2 +- .../home-manager-modules/programs/firefox.nix | 2 +- .../home-manager-modules/programs/freecad.nix | 2 +- .../home-manager-modules/programs/gimp.nix | 2 +- .../home-manager-modules/programs/idea.nix | 2 +- .../programs/inkscape.nix | 2 +- .../programs/kdenlive.nix | 2 +- .../home-manager-modules/programs/krita.nix | 2 +- .../programs/libreoffice.nix | 2 +- .../home-manager-modules/programs/makemkv.nix | 2 +- .../programs/mapillary-uploader.nix | 2 +- modules/home-manager-modules/programs/obs.nix | 2 +- .../programs/obsidian.nix | 2 +- .../home-manager-modules/programs/olympus.nix | 2 +- .../home-manager-modules/programs/openrgb.nix | 2 +- .../home-manager-modules/programs/picard.nix | 2 +- .../programs/prostudiomasters.nix | 2 +- .../programs/protonvpn.nix | 2 +- .../programs/qbittorrent.nix | 2 +- .../programs/qflipper.nix | 2 +- .../home-manager-modules/programs/signal.nix | 2 +- .../home-manager-modules/programs/steam.nix | 2 +- .../programs/tor-browser.nix | 2 +- .../programs/ungoogled-chromium.nix | 2 +- modules/home-manager-modules/programs/via.nix | 2 +- .../programs/vmware-workstation.nix | 2 +- modules/nixos-modules/ollama/storage.nix | 2 +- .../nixos-modules/server/actual/storage.nix | 2 +- .../nixos-modules/server/bazarr/storage.nix | 2 +- .../server/crab-hole/storage.nix | 2 +- .../nixos-modules/server/fail2ban/storage.nix | 2 +- .../server/flaresolverr/storage.nix | 2 +- .../nixos-modules/server/forgejo/storage.nix | 2 +- .../server/home-assistant/storage.nix | 2 +- .../nixos-modules/server/immich/storage.nix | 2 +- .../nixos-modules/server/jackett/storage.nix | 2 +- .../nixos-modules/server/jellyfin/storage.nix | 2 +- .../nixos-modules/server/lidarr/storage.nix | 2 +- .../network_storage/network_storage.nix | 2 +- .../server/panoramax/storage.nix | 2 +- .../server/paperless/storage.nix | 2 +- .../nixos-modules/server/postgres/storage.nix | 2 +- .../server/qbittorent/storage.nix | 2 +- .../nixos-modules/server/radarr/storage.nix | 2 +- .../server/reverseProxy/storage.nix | 2 +- .../nixos-modules/server/sonarr/storage.nix | 2 +- modules/nixos-modules/server/wyoming.nix | 2 +- modules/nixos-modules/ssh.nix | 2 +- .../nixos-modules/storage/impermanence.nix | 4 +-- modules/nixos-modules/storage/storage.nix | 25 +++++++++++-------- modules/nixos-modules/storage/zfs.nix | 4 +-- modules/nixos-modules/sync/storage.nix | 2 +- modules/nixos-modules/tailscale/storage.nix | 2 +- modules/nixos-modules/users.nix | 8 +++--- 63 files changed, 81 insertions(+), 78 deletions(-) diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index c61d693..4a58cbb 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.impermanence.enable) { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "desktop" "downloads" diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index 67f0ee4..f5e9869 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -26,7 +26,7 @@ in { # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = ["."]; }; }) diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix index 213ad67..3e723c9 100644 --- a/modules/home-manager-modules/openssh.nix +++ b/modules/home-manager-modules/openssh.nix @@ -96,7 +96,7 @@ } ) (lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { files = lib.lists.flatten ( builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys ); diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix index 739245e..c54feac 100644 --- a/modules/home-manager-modules/programs/anki.nix +++ b/modules/home-manager-modules/programs/anki.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ ".local/share/Anki2" ]; diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index c752669..ade24b6 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/Bitwarden" ]; diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index 768299b..ced1998 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/bruno/" ]; diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index 14e48dc..74a0cf4 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/calibre" ]; diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix index 49c7c47..c17c8b0 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.configHome}/blackmagic" diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index abc7c29..f509646 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.dataHome}/DBeaverData/" ]; diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index c62de57..3f5d72c 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/discord/" ]; diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix index 282c022..e100200 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -25,7 +25,7 @@ }; in { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { - home.persistence."/persist/home" = lib.mkMerge ( + home.persistence."/persist/replicate/home" = lib.mkMerge ( ( lib.attrsets.mapAttrsToList (profile: _: buildProfilePersistence profile) diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index c546794..19e08fa 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/FreeCAD" ]; diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index c127234..fbe4471 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/GIMP" ]; diff --git a/modules/home-manager-modules/programs/idea.nix b/modules/home-manager-modules/programs/idea.nix index 438e345..ec9d7d6 100644 --- a/modules/home-manager-modules/programs/idea.nix +++ b/modules/home-manager-modules/programs/idea.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ # configuration "${config.xdg.configHome}/JetBrains/" diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index 8cef5cb..67e5f80 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/inkscape" ]; diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix index a130fb8..2bec5b3 100644 --- a/modules/home-manager-modules/programs/kdenlive.nix +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/kdenliverc" "${config.xdg.dataHome}/kdenlive" diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index 869b10b..88d1de9 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/kritarc" "${config.xdg.dataHome}/krita" diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index 924d2a1..9c3537f 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/libreoffice" ]; diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix index e158c07..9fcde8b 100644 --- a/modules/home-manager-modules/programs/makemkv.nix +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -30,7 +30,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ ".MakeMKV" ]; diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index 7fb416e..09894c9 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -17,7 +17,7 @@ in { } ( mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader" diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index 5e226cc..3a099f7 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/obs-studio" ]; diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix index 91b59b3..e07beab 100644 --- a/modules/home-manager-modules/programs/obsidian.nix +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/obsidian" ]; diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix index 2be0084..3223d62 100644 --- a/modules/home-manager-modules/programs/olympus.nix +++ b/modules/home-manager-modules/programs/olympus.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/olympus" "${config.xdg.dataHome}/olympus" diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index 94636fc..64d6229 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/OpenRGB" ]; diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index a6a7887..5d197f8 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/MusicBrainz" ]; diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix index 3653ae4..5256f26 100644 --- a/modules/home-manager-modules/programs/prostudiomasters.nix +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/ProStudioMasters" ]; diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix index d04c012..57e50ab 100644 --- a/modules/home-manager-modules/programs/protonvpn.nix +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/protonvpn" "${config.xdg.configHome}/Proton" diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix index 37fd464..ee098e0 100644 --- a/modules/home-manager-modules/programs/qbittorrent.nix +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/qBittorrent" ]; diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index 8261f5e..0c7d242 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/qFlipper" ]; diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index 3dae867..962a139 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/Signal" ]; diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index 98b970f..3dd6504 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -18,7 +18,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ { directory = "${config.xdg.dataHome}/Steam"; diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index e13dd4c..92484ae 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.dataHome}/torbrowser" ]; diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix index 8ca8ec0..e76eeeb 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/chromium" ]; diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index acf2d8c..3a638aa 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ "${config.xdg.configHome}/via" "${config.xdg.dataHome}/via" diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix index 30ae692..277e4bd 100644 --- a/modules/home-manager-modules/programs/vmware-workstation.nix +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -17,7 +17,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/home" = { + home.persistence."/persist/replicate/home" = { directories = [ { directory = ".vmware"; diff --git a/modules/nixos-modules/ollama/storage.nix b/modules/nixos-modules/ollama/storage.nix index ff2348e..65bbe26 100644 --- a/modules/nixos-modules/ollama/storage.nix +++ b/modules/nixos-modules/ollama/storage.nix @@ -20,7 +20,7 @@ # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.ollama.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."/var/lib/private/ollama" = { enable = true; owner.name = config.services.ollama.user; diff --git a/modules/nixos-modules/server/actual/storage.nix b/modules/nixos-modules/server/actual/storage.nix index eab0817..cec2eab 100644 --- a/modules/nixos-modules/server/actual/storage.nix +++ b/modules/nixos-modules/server/actual/storage.nix @@ -29,7 +29,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.actual.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${dataDirectory}" = { owner.name = "actual"; group.name = "actual"; diff --git a/modules/nixos-modules/server/bazarr/storage.nix b/modules/nixos-modules/server/bazarr/storage.nix index 53a9d9c..c8c7d1d 100644 --- a/modules/nixos-modules/server/bazarr/storage.nix +++ b/modules/nixos-modules/server/bazarr/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.bazarr.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${bazarr_data_directory}" = { owner.name = "bazarr"; group.name = "bazarr"; diff --git a/modules/nixos-modules/server/crab-hole/storage.nix b/modules/nixos-modules/server/crab-hole/storage.nix index ec38846..caacdf8 100644 --- a/modules/nixos-modules/server/crab-hole/storage.nix +++ b/modules/nixos-modules/server/crab-hole/storage.nix @@ -25,7 +25,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.crab-hole.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${workingDirectory}" = { owner.name = "crab-hole"; group.name = "crab-hole"; diff --git a/modules/nixos-modules/server/fail2ban/storage.nix b/modules/nixos-modules/server/fail2ban/storage.nix index 6c1f227..02ad3f0 100644 --- a/modules/nixos-modules/server/fail2ban/storage.nix +++ b/modules/nixos-modules/server/fail2ban/storage.nix @@ -25,7 +25,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.fail2ban.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${dataFolder}" = { owner.name = "fail2ban"; group.name = "fail2ban"; diff --git a/modules/nixos-modules/server/flaresolverr/storage.nix b/modules/nixos-modules/server/flaresolverr/storage.nix index 657bcc6..da52480 100644 --- a/modules/nixos-modules/server/flaresolverr/storage.nix +++ b/modules/nixos-modules/server/flaresolverr/storage.nix @@ -14,7 +14,7 @@ # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.flaresolverr.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."/var/lib/flaresolverr" = { owner.name = "flaresolverr"; group.name = "flaresolverr"; diff --git a/modules/nixos-modules/server/forgejo/storage.nix b/modules/nixos-modules/server/forgejo/storage.nix index 31304e7..d7b54b9 100644 --- a/modules/nixos-modules/server/forgejo/storage.nix +++ b/modules/nixos-modules/server/forgejo/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.forgejo.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${stateDir}" = { owner.name = "forgejo"; group.name = "forgejo"; diff --git a/modules/nixos-modules/server/home-assistant/storage.nix b/modules/nixos-modules/server/home-assistant/storage.nix index 231387b..00831c4 100644 --- a/modules/nixos-modules/server/home-assistant/storage.nix +++ b/modules/nixos-modules/server/home-assistant/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.home-assistant.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${configDir}" = { owner.name = "hass"; group.name = "hass"; diff --git a/modules/nixos-modules/server/immich/storage.nix b/modules/nixos-modules/server/immich/storage.nix index 65b4bed..cd9f935 100644 --- a/modules/nixos-modules/server/immich/storage.nix +++ b/modules/nixos-modules/server/immich/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.immich.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${mediaLocation}" = { owner.name = "immich"; group.name = "immich"; diff --git a/modules/nixos-modules/server/jackett/storage.nix b/modules/nixos-modules/server/jackett/storage.nix index 6056c9c..eaa0bc9 100644 --- a/modules/nixos-modules/server/jackett/storage.nix +++ b/modules/nixos-modules/server/jackett/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.jackett.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${jackett_data_directory}" = { owner.name = "jackett"; group.name = "jackett"; diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix index 867b936..2854cb8 100644 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -30,7 +30,7 @@ in { }) (lib.mkIf config.services.jellyfin.impermanence.enable { storage.impermanence.datasets = { - "persist/system/root" = { + "persist/replicate/system/root" = { directories = { "${jellyfin_data_directory}" = { enable = true; diff --git a/modules/nixos-modules/server/lidarr/storage.nix b/modules/nixos-modules/server/lidarr/storage.nix index 9d818ff..57c568c 100644 --- a/modules/nixos-modules/server/lidarr/storage.nix +++ b/modules/nixos-modules/server/lidarr/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.lidarr.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${lidarr_data_directory}" = { owner.name = "lidarr"; group.name = "lidarr"; diff --git a/modules/nixos-modules/server/network_storage/network_storage.nix b/modules/nixos-modules/server/network_storage/network_storage.nix index ebc3bee..b9d0446 100644 --- a/modules/nixos-modules/server/network_storage/network_storage.nix +++ b/modules/nixos-modules/server/network_storage/network_storage.nix @@ -74,7 +74,7 @@ in { ); } # (lib.mkIf config.host.impermanence.enable { - # environment.persistence."/persist/system/root" = { + # environment.persistence."/persist/replicate/system/root" = { # enable = true; # hideMounts = true; # directories = [ diff --git a/modules/nixos-modules/server/panoramax/storage.nix b/modules/nixos-modules/server/panoramax/storage.nix index 52d9d74..164c9bb 100644 --- a/modules/nixos-modules/server/panoramax/storage.nix +++ b/modules/nixos-modules/server/panoramax/storage.nix @@ -21,7 +21,7 @@ # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.panoramax.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."/var/lib/panoramax" = { owner.name = "panoramax"; group.name = "panoramax"; diff --git a/modules/nixos-modules/server/paperless/storage.nix b/modules/nixos-modules/server/paperless/storage.nix index 6f74441..381e45b 100644 --- a/modules/nixos-modules/server/paperless/storage.nix +++ b/modules/nixos-modules/server/paperless/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.paperless.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${dataDir}" = { owner.name = "paperless"; group.name = "paperless"; diff --git a/modules/nixos-modules/server/postgres/storage.nix b/modules/nixos-modules/server/postgres/storage.nix index 0ec0eb2..068c93f 100644 --- a/modules/nixos-modules/server/postgres/storage.nix +++ b/modules/nixos-modules/server/postgres/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.postgresql.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${dataDir}" = { owner.name = "postgres"; group.name = "postgres"; diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix index 02d4757..0bb01e3 100644 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -27,7 +27,7 @@ in { lib.mkIf config.services.qbittorrent.impermanence.enable { storage.impermanence.datasets = { - "persist/system/root" = { + "persist/replicate/system/root" = { directories."${qbittorent_profile_directory}" = { owner.name = "qbittorrent"; group.name = "qbittorrent"; diff --git a/modules/nixos-modules/server/radarr/storage.nix b/modules/nixos-modules/server/radarr/storage.nix index 82d2bf8..a9b4901 100644 --- a/modules/nixos-modules/server/radarr/storage.nix +++ b/modules/nixos-modules/server/radarr/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.radarr.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${radarr_data_directory}" = { owner.name = "radarr"; group.name = "radarr"; diff --git a/modules/nixos-modules/server/reverseProxy/storage.nix b/modules/nixos-modules/server/reverseProxy/storage.nix index c4ee04a..29a3120 100644 --- a/modules/nixos-modules/server/reverseProxy/storage.nix +++ b/modules/nixos-modules/server/reverseProxy/storage.nix @@ -16,7 +16,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.reverseProxy.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${dataDir}" = { owner.name = "acme"; group.name = "acme"; diff --git a/modules/nixos-modules/server/sonarr/storage.nix b/modules/nixos-modules/server/sonarr/storage.nix index c74a7b8..aebd0a9 100644 --- a/modules/nixos-modules/server/sonarr/storage.nix +++ b/modules/nixos-modules/server/sonarr/storage.nix @@ -24,7 +24,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.sonarr.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${sonarr_data_directory}" = { owner.name = "sonarr"; group.name = "sonarr"; diff --git a/modules/nixos-modules/server/wyoming.nix b/modules/nixos-modules/server/wyoming.nix index c9a1474..1df6877 100644 --- a/modules/nixos-modules/server/wyoming.nix +++ b/modules/nixos-modules/server/wyoming.nix @@ -48,7 +48,7 @@ systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all"; } (lib.mkIf config.host.impermanence.enable { - environment.persistence."/persist/system/root" = { + environment.persistence."/persist/replicate/system/root" = { enable = true; hideMounts = true; directories = [ diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix index 20e7881..dd70918 100644 --- a/modules/nixos-modules/ssh.nix +++ b/modules/nixos-modules/ssh.nix @@ -32,7 +32,7 @@ # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.openssh.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { files = builtins.listToAttrs ( lib.lists.flatten ( builtins.map (hostKey: [ diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 4f231bf..db0deb0 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -72,8 +72,8 @@ in { "var-lib-private-permissions" = { deps = ["specialfs"]; text = '' - mkdir -p /persist/system/root/var/lib/private - chmod 0700 /persist/system/root/var/lib/private + mkdir -p /persist/replicate/system/root/var/lib/private + chmod 0700 /persist/replicate/system/root/var/lib/private ''; }; }; diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index e9f740b..2708f6b 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -10,7 +10,7 @@ lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { storage.zfs.datasets = { - "persist/system/nix" = { + "persist/local/nix" = { type = "zfs_fs"; mount = { enable = true; @@ -22,15 +22,12 @@ atime = "off"; relatime = "off"; }; - "persist/system/var/log" = { + "persist/replicate/system/var/log" = { type = "zfs_fs"; mount = { enable = true; mountPoint = "/var/log"; }; - snapshot = { - autoSnapshot = false; - }; }; }; } @@ -53,11 +50,11 @@ }) (lib.mkIf config.storage.impermanence.enable { boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/local/system/root@blank + zfs rollback -r rpool/ephemeral/system/root@blank ''; storage.zfs.datasets = { - "local/system/root" = { + "ephemeral/system/root" = { type = "zfs_fs"; mount = { enable = true; @@ -70,10 +67,13 @@ }; storage.impermanence.datasets = { - "persist/system/root" = { + "persist/replicate/system/root" = { mount = { enable = true; - mountPoint = "/persist/system/root"; + mountPoint = "/persist/replicate/system/root"; + }; + snapshot = { + autoSnapshot = true; }; directories = { "/var/lib/nixos".enable = true; @@ -83,10 +83,13 @@ "/etc/machine-id".enable = true; }; }; - "persist/home" = { + "persist/replicate/home" = { mount = { enable = true; - mountPoint = "/persist/home"; + mountPoint = "/persist/replicate/home"; + }; + snapshot = { + autoSnapshot = true; }; }; }; diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 451e226..1942e8d 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -170,7 +170,7 @@ in { }; cache = lib.mkOption { type = lib.types.listOf deviceType; - default = {}; + default = []; }; }; @@ -264,7 +264,7 @@ in { members = builtins.map (device: hashDisk device.device) vdev; }) config.storage.zfs.pool.vdevs; - cache = builtins.map (device: hashDisk device.device) (builtins.attrValues config.storage.zfs.pool.cache); + cache = builtins.map (device: hashDisk device.device) config.storage.zfs.pool.cache; }; }; diff --git a/modules/nixos-modules/sync/storage.nix b/modules/nixos-modules/sync/storage.nix index a58a49f..7532045 100644 --- a/modules/nixos-modules/sync/storage.nix +++ b/modules/nixos-modules/sync/storage.nix @@ -36,7 +36,7 @@ in { }) config.services.syncthing.settings.folders; - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories = { "${mountDir}" = { enable = true; diff --git a/modules/nixos-modules/tailscale/storage.nix b/modules/nixos-modules/tailscale/storage.nix index 9533aef..a417aaf 100644 --- a/modules/nixos-modules/tailscale/storage.nix +++ b/modules/nixos-modules/tailscale/storage.nix @@ -22,7 +22,7 @@ in { # TODO: placeholder to configure a unique dataset for this service }) (lib.mkIf config.services.tailscale.impermanence.enable { - storage.impermanence.datasets."persist/system/root" = { + storage.impermanence.datasets."persist/replicate/system/root" = { directories."${tailscale_data_directory}" = { enable = true; owner.name = "root"; diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index bf45ac9..241ab6c 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -405,7 +405,7 @@ in { # sops age key needs to be available to pre persist for user generation storage.zfs.datasets = lib.mkMerge [ { - "local/system/sops" = { + "persist/local/system/sops" = { type = "zfs_fs"; mount = { enable = true; @@ -417,7 +417,7 @@ in { } (lib.mkMerge ( builtins.map (user: { - "local/home/${user.name}" = { + "ephemeral/home/${user.name}" = { type = "zfs_fs"; mount = { enable = true; @@ -432,7 +432,7 @@ in { # Post resume commands to rollback user home datasets to blank snapshots boot.initrd.postResumeCommands = lib.mkAfter ( - lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") normalUsers) ); @@ -440,7 +440,7 @@ in { systemd = { tmpfiles.rules = builtins.map ( - user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" + user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" ) normalUsers; }; From f8edad75bf526a819a698a6df813bcecc2c06b4f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 14 Nov 2025 22:06:32 -0600 Subject: [PATCH 24/44] feat: updated user configs to better match original config --- modules/nixos-modules/storage/storage.nix | 14 +++ .../storage/submodules/dataset.nix | 6 +- modules/nixos-modules/users.nix | 104 ++++++++++-------- 3 files changed, 73 insertions(+), 51 deletions(-) diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 2708f6b..be514d7 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -10,6 +10,16 @@ lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { storage.zfs.datasets = { + "persist" = { + type = "zfs_fs"; + }; + "persist/local" = { + type = "zfs_fs"; + }; + "persist/replicate" = { + type = "zfs_fs"; + }; + "persist/local/nix" = { type = "zfs_fs"; mount = { @@ -22,6 +32,7 @@ atime = "off"; relatime = "off"; }; + "persist/replicate/system/var/log" = { type = "zfs_fs"; mount = { @@ -54,6 +65,9 @@ ''; storage.zfs.datasets = { + "ephemeral" = { + type = "zfs_fs"; + }; "ephemeral/system/root" = { type = "zfs_fs"; mount = { diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index 3de7719..5199f98 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -73,11 +73,7 @@ snapshot = { # This option should set this option flag # "com.sun:auto-snapshot" = "false"; - autoSnapshot = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Enable automatic snapshots for this dataset"; - }; + autoSnapshot = lib.mkEnableOption "Enable automatic snapshots for this dataset"; # Creates a blank snapshot in the post create hook for rollback purposes blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; }; diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 241ab6c..ab123b9 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -399,53 +399,65 @@ in { }; }; } - (lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # sops age key needs to be available to pre persist for user generation - storage.zfs.datasets = lib.mkMerge [ - { - "persist/local/system/sops" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = SOPS_AGE_KEY_DIRECTORY; - }; - atime = "off"; - relatime = "off"; - }; - } - (lib.mkMerge ( - builtins.map (user: { - "ephemeral/home/${user.name}" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/home/${user.name}"; - }; - snapshot.blankSnapshot = true; - }; - }) - normalUsers - )) - ]; - - # Post resume commands to rollback user home datasets to blank snapshots - boot.initrd.postResumeCommands = lib.mkAfter ( - lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") - normalUsers) - ); - - # Create persist home directories with proper permissions - systemd = { - tmpfiles.rules = - builtins.map ( - user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" - ) - normalUsers; + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # sops age key needs to be available to pre persist for user generation + storage.zfs.datasets."persist/local/system/sops" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = SOPS_AGE_KEY_DIRECTORY; }; - } - ])) + atime = "off"; + relatime = "off"; + }; + } + (lib.mkIf (!config.storage.impermanence.enable) { + storage.zfs.datasets = lib.mkMerge ( + builtins.map (user: { + "persist/replicate/home/${user.name}" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/home/${user.name}"; + }; + snapshot.autoSnapshot = true; + }; + }) + normalUsers + ); + }) + (lib.mkIf config.storage.impermanence.enable { + storage.zfs.datasets = lib.mkMerge ( + builtins.map (user: { + "ephemeral/home/${user.name}" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/home/${user.name}"; + }; + snapshot.blankSnapshot = true; + }; + }) + normalUsers + ); + + # Post resume commands to rollback user home datasets to blank snapshots + boot.initrd.postResumeCommands = lib.mkAfter ( + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") + normalUsers) + ); + + # TODO: I don't think we need this anymore but I have not tested it + # Create persist home directories with proper permissions + # systemd = { + # tmpfiles.rules = + # builtins.map ( + # user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" + # ) + # normalUsers; + # }; + }) ])) ]; } From 757a3892e12f58ab078341c19e1d6172ef318e3b Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 15 Nov 2025 13:39:53 -0600 Subject: [PATCH 25/44] feat: updated interface for storage --- .../nixos-modules/server/jellyfin/storage.nix | 2 +- .../server/qbittorent/storage.nix | 2 +- modules/nixos-modules/storage/storage.nix | 287 ++++++++++++------ .../storage/submodules/dataset.nix | 1 - 4 files changed, 196 insertions(+), 96 deletions(-) diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix index 2854cb8..79d0605 100644 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -44,7 +44,7 @@ in { }; }; }; - "persist/system/jellyfin" = { + "persist/replicate/system/jellyfin" = { atime = "off"; relatime = "off"; diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix index 0bb01e3..32244ca 100644 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -33,7 +33,7 @@ in { group.name = "qbittorrent"; }; }; - "persist/system/qbittorrent" = { + "persist/replicate/system/qbittorrent" = { directories."${config.services.qbittorrent.mediaDir}" = { owner.name = "qbittorrent"; group.name = "qbittorrent"; diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index be514d7..7d14dd7 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -1,49 +1,155 @@ -{ +args @ { lib, config, ... -}: { - # TODO: create all of the datasets from option and home-manager datasets - # TODO: set up datasets for systemd services that want a dataset created +}: let + datasetSubmodule = (import ./submodules/dataset.nix) args; + impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; + + # Get the option names from both submodules to automatically determine which are impermanence-specific + regularDatasetEval = lib.evalModules { + modules = [datasetSubmodule]; + specialArgs = args; + }; + impermanenceDatasetEval = lib.evalModules { + modules = [impermanenceDatasetSubmodule]; + specialArgs = args; + }; + + regularDatasetOptions = builtins.attrNames regularDatasetEval.options; + impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; + + # Find options that are only in impermanence datasets (not in regular ZFS datasets) + impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; +in { + options.storage.datasets = { + ephemeral = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + default = {}; + }; + local = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + replicate = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + }; + config = lib.mkMerge [ - ( - lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - storage.zfs.datasets = { - "persist" = { - type = "zfs_fs"; + (lib.mkIf config.storage.zfs.enable { + # Create ZFS datasets based on storage.datasets configuration + }) + (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable) { + storage.datasets = { + ephemeral = { + "" = { + type = "zfs_fs"; + }; + "system/root" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/"; }; - "persist/local" = { - type = "zfs_fs"; - }; - "persist/replicate" = { - type = "zfs_fs"; - }; - - "persist/local/nix" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/nix"; - }; - snapshot = { - autoSnapshot = false; - }; - atime = "off"; - relatime = "off"; - }; - - "persist/replicate/system/var/log" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/var/log"; - }; + snapshot = { + blankSnapshot = true; }; }; - } - (lib.mkIf (!config.storage.impermanence.enable) { - storage.zfs.rootDataset = { + }; + local = { + "nix" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/nix"; + }; + snapshot = { + autoSnapshot = false; + }; + atime = "off"; + relatime = "off"; + }; + }; + replicate = { + "system/var/log" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/var/log"; + }; + }; + "system/root" = { + mount = { + enable = true; + mountPoint = "/persist/replicate/system/root"; + }; + snapshot = { + autoSnapshot = true; + }; + directories = { + "/var/lib/nixos".enable = true; + "/var/lib/systemd/coredump".enable = true; + }; + files = { + "/etc/machine-id".enable = true; + }; + }; + "home" = { + mount = { + enable = true; + mountPoint = "/persist/replicate/home"; + }; + snapshot = { + autoSnapshot = true; + }; + }; + }; + }; + + storage.zfs.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "ephemeral" + else "ephemeral/${name}"; + value = dataset; + }) + config.storage.datasets.ephemeral) + ]; + + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/ephemeral/system/root@blank + ''; + + storage.impermanence.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/local" + else "persist/local/${name}"; + value = dataset; + }) + config.storage.datasets.local) + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/replicate" + else "persist/replicate/${name}"; + value = dataset; + }) + config.storage.datasets.replicate) + ]; + }) + (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable) { + storage.datasets = { + # Base organizational datasets (only needed when impermanence is disabled) + local = { + "" = { + type = "zfs_fs"; + }; + "root" = { type = "zfs_fs"; mount = { enable = true; @@ -58,63 +164,58 @@ blankSnapshot = true; }; }; - }) - (lib.mkIf config.storage.impermanence.enable { - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/ephemeral/system/root@blank - ''; - - storage.zfs.datasets = { - "ephemeral" = { - type = "zfs_fs"; + "nix" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/nix"; }; - "ephemeral/system/root" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/"; - }; - snapshot = { - blankSnapshot = true; - }; + snapshot = { + autoSnapshot = false; + }; + atime = "off"; + relatime = "off"; + }; + }; + replicate = { + "" = { + type = "zfs_fs"; + }; + "system/var/log" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/var/log"; }; }; + }; + }; - storage.impermanence.datasets = { - "persist/replicate/system/root" = { - mount = { - enable = true; - mountPoint = "/persist/replicate/system/root"; - }; - snapshot = { - autoSnapshot = true; - }; - directories = { - "/var/lib/nixos".enable = true; - "/var/lib/systemd/coredump".enable = true; - }; - files = { - "/etc/machine-id".enable = true; - }; - }; - "persist/replicate/home" = { - mount = { - enable = true; - mountPoint = "/persist/replicate/home"; - }; - snapshot = { - autoSnapshot = true; - }; - }; - }; - - # TODO: home-manager.users..storage.impermanence.enable - # is false then persist the entire directory of the user - # if true persist home-manager.users..storage.impermanence.datasets - # TODO: systemd.services..storage.datasets persists - }) - ]) - ) - # TODO: configure other needed storage modes here + storage.zfs.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/local" + else "persist/local/${name}"; + value = builtins.removeAttrs dataset impermanenceOnlyOptions; + }) + config.storage.datasets.local) + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/replicate" + else "persist/replicate/${name}"; + value = builtins.removeAttrs dataset impermanenceOnlyOptions; + }) + config.storage.datasets.replicate) + ]; + }) ]; + + # TODO: set up datasets for systemd services that want a dataset created + # TODO: home-manager.users..storage.impermanence.enable + # is false then persist the entire directory of the user + # if true persist home-manager.users..storage.impermanence.datasets + # TODO: systemd.services..storage.datasets persists + # TODO: configure other needed storage modes here } diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index 5199f98..0b57886 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -72,7 +72,6 @@ snapshot = { # This option should set this option flag - # "com.sun:auto-snapshot" = "false"; autoSnapshot = lib.mkEnableOption "Enable automatic snapshots for this dataset"; # Creates a blank snapshot in the post create hook for rollback purposes blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; From c2701ea8f0605cb8744ab53bf206a756685bc396 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 15 Nov 2025 16:37:10 -0600 Subject: [PATCH 26/44] feat: moved services over to using the new storage datasets --- modules/nixos-modules/ollama/storage.nix | 60 +++++------ .../nixos-modules/server/actual/storage.nix | 35 ++----- .../nixos-modules/server/bazarr/storage.nix | 31 ++---- .../server/crab-hole/storage.nix | 32 ++---- .../nixos-modules/server/fail2ban/storage.nix | 31 ++---- .../server/flaresolverr/storage.nix | 23 ++--- .../nixos-modules/server/forgejo/storage.nix | 31 ++---- .../server/home-assistant/storage.nix | 31 ++---- .../nixos-modules/server/immich/storage.nix | 31 ++---- .../nixos-modules/server/jackett/storage.nix | 31 ++---- .../nixos-modules/server/jellyfin/storage.nix | 99 ++++++++----------- .../nixos-modules/server/lidarr/storage.nix | 31 ++---- .../server/panoramax/storage.nix | 30 ++---- .../server/paperless/storage.nix | 31 ++---- .../nixos-modules/server/postgres/storage.nix | 31 ++---- .../server/qbittorent/storage.nix | 78 ++++++--------- .../nixos-modules/server/radarr/storage.nix | 31 ++---- .../server/reverseProxy/storage.nix | 23 ++--- .../nixos-modules/server/sonarr/storage.nix | 31 ++---- modules/nixos-modules/ssh.nix | 69 ++++++------- modules/nixos-modules/sync/storage.nix | 57 +++-------- modules/nixos-modules/tailscale/storage.nix | 30 ++---- modules/nixos-modules/users.nix | 10 +- 23 files changed, 281 insertions(+), 606 deletions(-) diff --git a/modules/nixos-modules/ollama/storage.nix b/modules/nixos-modules/ollama/storage.nix index 65bbe26..6ab0fc8 100644 --- a/modules/nixos-modules/ollama/storage.nix +++ b/modules/nixos-modules/ollama/storage.nix @@ -10,40 +10,28 @@ }; }; - config = lib.mkIf config.services.ollama.enable ( - lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # Ollama needs persistent storage for models and configuration - } - (lib.mkIf (!config.services.ollama.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.ollama.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."/var/lib/private/ollama" = { - enable = true; - owner.name = config.services.ollama.user; - group.name = config.services.ollama.group; - owner.permissions = { - read = true; - write = true; - execute = false; - }; - group.permissions = { - read = false; - write = false; - execute = false; - }; - other.permissions = { - read = false; - write = false; - execute = false; - }; - }; - }; - }) - ])) - ] - ); + config = lib.mkIf (config.services.ollama.enable) { + storage.datasets.replicate."system/root" = { + directories."/var/lib/private/ollama" = lib.mkIf config.services.ollama.impermanence.enable { + enable = true; + owner.name = config.services.ollama.user; + group.name = config.services.ollama.group; + owner.permissions = { + read = true; + write = true; + execute = false; + }; + group.permissions = { + read = false; + write = false; + execute = false; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }; } diff --git a/modules/nixos-modules/server/actual/storage.nix b/modules/nixos-modules/server/actual/storage.nix index cec2eab..d6b904e 100644 --- a/modules/nixos-modules/server/actual/storage.nix +++ b/modules/nixos-modules/server/actual/storage.nix @@ -11,31 +11,12 @@ in { default = config.services.actual.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.actual.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.actual.settings.dataDir == dataDirectory; - message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}"; - } - { - assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false; - message = "actual systemd service must have DynamicUser enabled to use private directory"; - } - ]; - } - (lib.mkIf (!config.services.actual.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.actual.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${dataDirectory}" = { - owner.name = "actual"; - group.name = "actual"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.actual.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDirectory}" = lib.mkIf config.services.actual.impermanence.enable { + owner.name = "actual"; + group.name = "actual"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/bazarr/storage.nix b/modules/nixos-modules/server/bazarr/storage.nix index c8c7d1d..a243d4c 100644 --- a/modules/nixos-modules/server/bazarr/storage.nix +++ b/modules/nixos-modules/server/bazarr/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.bazarr.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.bazarr.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.bazarr.dataDir == bazarr_data_directory; - message = "bazarr data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.bazarr.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.bazarr.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${bazarr_data_directory}" = { - owner.name = "bazarr"; - group.name = "bazarr"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.bazarr.enable { + storage.datasets.replicate."system/root" = { + directories."${bazarr_data_directory}" = lib.mkIf config.services.bazarr.impermanence.enable { + owner.name = "bazarr"; + group.name = "bazarr"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/crab-hole/storage.nix b/modules/nixos-modules/server/crab-hole/storage.nix index caacdf8..827fb25 100644 --- a/modules/nixos-modules/server/crab-hole/storage.nix +++ b/modules/nixos-modules/server/crab-hole/storage.nix @@ -10,28 +10,12 @@ in { default = config.services.crab-hole.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.crab-hole.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = - config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory); - message = "crab-hole working directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.crab-hole.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.crab-hole.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${workingDirectory}" = { - owner.name = "crab-hole"; - group.name = "crab-hole"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.crab-hole.enable { + storage.datasets.replicate."system/root" = { + directories."${workingDirectory}" = lib.mkIf config.services.crab-hole.impermanence.enable { + owner.name = "crab-hole"; + group.name = "crab-hole"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/fail2ban/storage.nix b/modules/nixos-modules/server/fail2ban/storage.nix index 02ad3f0..1ef02c7 100644 --- a/modules/nixos-modules/server/fail2ban/storage.nix +++ b/modules/nixos-modules/server/fail2ban/storage.nix @@ -11,27 +11,12 @@ in { default = config.services.fail2ban.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.fail2ban.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; - message = "fail2ban data file does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.fail2ban.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.fail2ban.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${dataFolder}" = { - owner.name = "fail2ban"; - group.name = "fail2ban"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.fail2ban.enable { + storage.datasets.replicate."system/root" = { + directories."${dataFolder}" = lib.mkIf config.services.fail2ban.impermanence.enable { + owner.name = "fail2ban"; + group.name = "fail2ban"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/flaresolverr/storage.nix b/modules/nixos-modules/server/flaresolverr/storage.nix index da52480..919318c 100644 --- a/modules/nixos-modules/server/flaresolverr/storage.nix +++ b/modules/nixos-modules/server/flaresolverr/storage.nix @@ -8,19 +8,12 @@ default = config.services.flaresolverr.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.flaresolverr.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - (lib.mkIf (!config.services.flaresolverr.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.flaresolverr.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."/var/lib/flaresolverr" = { - owner.name = "flaresolverr"; - group.name = "flaresolverr"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.flaresolverr.enable { + storage.datasets.replicate."system/root" = { + directories."/var/lib/flaresolverr" = lib.mkIf config.services.flaresolverr.impermanence.enable { + owner.name = "flaresolverr"; + group.name = "flaresolverr"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/forgejo/storage.nix b/modules/nixos-modules/server/forgejo/storage.nix index d7b54b9..da30ed9 100644 --- a/modules/nixos-modules/server/forgejo/storage.nix +++ b/modules/nixos-modules/server/forgejo/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.forgejo.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.forgejo.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.forgejo.stateDir == stateDir; - message = "forgejo state directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.forgejo.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.forgejo.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${stateDir}" = { - owner.name = "forgejo"; - group.name = "forgejo"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.forgejo.enable { + storage.datasets.replicate."system/root" = { + directories."${stateDir}" = lib.mkIf config.services.forgejo.impermanence.enable { + owner.name = "forgejo"; + group.name = "forgejo"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/home-assistant/storage.nix b/modules/nixos-modules/server/home-assistant/storage.nix index 00831c4..60e5085 100644 --- a/modules/nixos-modules/server/home-assistant/storage.nix +++ b/modules/nixos-modules/server/home-assistant/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.home-assistant.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.home-assistant.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.home-assistant.configDir == configDir; - message = "home assistant config directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.home-assistant.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.home-assistant.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${configDir}" = { - owner.name = "hass"; - group.name = "hass"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.home-assistant.enable { + storage.datasets.replicate."system/root" = { + directories."${configDir}" = lib.mkIf config.services.home-assistant.impermanence.enable { + owner.name = "hass"; + group.name = "hass"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/immich/storage.nix b/modules/nixos-modules/server/immich/storage.nix index cd9f935..de24329 100644 --- a/modules/nixos-modules/server/immich/storage.nix +++ b/modules/nixos-modules/server/immich/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.immich.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.immich.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.immich.mediaLocation == mediaLocation; - message = "immich media location does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.immich.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.immich.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${mediaLocation}" = { - owner.name = "immich"; - group.name = "immich"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.immich.enable { + storage.datasets.replicate."system/root" = { + directories."${mediaLocation}" = lib.mkIf config.services.immich.impermanence.enable { + owner.name = "immich"; + group.name = "immich"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/jackett/storage.nix b/modules/nixos-modules/server/jackett/storage.nix index eaa0bc9..5f202e6 100644 --- a/modules/nixos-modules/server/jackett/storage.nix +++ b/modules/nixos-modules/server/jackett/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.jackett.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.jackett.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.jackett.dataDir == jackett_data_directory; - message = "jackett data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.jackett.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.jackett.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${jackett_data_directory}" = { - owner.name = "jackett"; - group.name = "jackett"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.jackett.enable { + storage.datasets.replicate."system/root" = { + directories."${jackett_data_directory}" = lib.mkIf config.services.jackett.impermanence.enable { + owner.name = "jackett"; + group.name = "jackett"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix index 79d0605..98f7a8c 100644 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -11,66 +11,47 @@ in { default = config.services.jellyfin.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.jellyfin.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.jellyfin.dataDir == jellyfin_data_directory; - message = "jellyfin data directory does not match persistence"; - } - { - assertion = config.services.jellyfin.cacheDir == jellyfin_cache_directory; - message = "jellyfin cache directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.jellyfin.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.jellyfin.impermanence.enable { - storage.impermanence.datasets = { - "persist/replicate/system/root" = { - directories = { - "${jellyfin_data_directory}" = { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - "${jellyfin_cache_directory}" = { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - }; + config = lib.mkIf config.services.jellyfin.enable { + storage.datasets.replicate = { + "system/root" = { + directories = { + "${jellyfin_data_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; }; - "persist/replicate/system/jellyfin" = { - atime = "off"; - relatime = "off"; - - directories."${config.services.jellyfin.media_directory}" = { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin_media"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = false; - write = false; - execute = false; - }; - }; + "${jellyfin_cache_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; }; }; - }) - ])) - ]); + }; + "system/jellyfin" = { + atime = "off"; + relatime = "off"; + + directories."${config.services.jellyfin.media_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin_media"; + owner.permissions = { + read = true; + write = true; + execute = true; + }; + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }; + }; } diff --git a/modules/nixos-modules/server/lidarr/storage.nix b/modules/nixos-modules/server/lidarr/storage.nix index 57c568c..c4c020e 100644 --- a/modules/nixos-modules/server/lidarr/storage.nix +++ b/modules/nixos-modules/server/lidarr/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.lidarr.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.lidarr.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.lidarr.dataDir == lidarr_data_directory; - message = "lidarr data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.lidarr.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.lidarr.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${lidarr_data_directory}" = { - owner.name = "lidarr"; - group.name = "lidarr"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.lidarr.enable { + storage.datasets.replicate."system/root" = { + directories."${lidarr_data_directory}" = lib.mkIf config.services.lidarr.impermanence.enable { + owner.name = "lidarr"; + group.name = "lidarr"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/panoramax/storage.nix b/modules/nixos-modules/server/panoramax/storage.nix index 164c9bb..b36e087 100644 --- a/modules/nixos-modules/server/panoramax/storage.nix +++ b/modules/nixos-modules/server/panoramax/storage.nix @@ -8,26 +8,12 @@ default = config.services.panoramax.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # TODO: configure impermanence for panoramax data - # This would typically include directories like: - # - /var/lib/panoramax - # - panoramax storage directories - # - any cache or temporary directories that need to persist - } - (lib.mkIf (!config.services.panoramax.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.panoramax.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."/var/lib/panoramax" = { - owner.name = "panoramax"; - group.name = "panoramax"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.panoramax.enable { + storage.datasets.replicate."system/root" = { + directories."/var/lib/panoramax" = lib.mkIf config.services.panoramax.impermanence.enable { + owner.name = "panoramax"; + group.name = "panoramax"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/paperless/storage.nix b/modules/nixos-modules/server/paperless/storage.nix index 381e45b..6e17bc2 100644 --- a/modules/nixos-modules/server/paperless/storage.nix +++ b/modules/nixos-modules/server/paperless/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.paperless.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.paperless.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.paperless.dataDir == dataDir; - message = "paperless data location does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.paperless.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.paperless.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${dataDir}" = { - owner.name = "paperless"; - group.name = "paperless"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.paperless.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.paperless.impermanence.enable { + owner.name = "paperless"; + group.name = "paperless"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/postgres/storage.nix b/modules/nixos-modules/server/postgres/storage.nix index 068c93f..58a84a6 100644 --- a/modules/nixos-modules/server/postgres/storage.nix +++ b/modules/nixos-modules/server/postgres/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.postgresql.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.postgresql.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.postgresql.dataDir == dataDir; - message = "postgres data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.postgresql.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.postgresql.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${dataDir}" = { - owner.name = "postgres"; - group.name = "postgres"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.postgresql.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.postgresql.impermanence.enable { + owner.name = "postgres"; + group.name = "postgres"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix index 32244ca..8dabab8 100644 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -10,53 +10,35 @@ in { default = config.services.qbittorrent.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.qbittorrent.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; - message = "qbittorrent data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.qbittorrent.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - ( - lib.mkIf config.services.qbittorrent.impermanence.enable - { - storage.impermanence.datasets = { - "persist/replicate/system/root" = { - directories."${qbittorent_profile_directory}" = { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - }; - }; - "persist/replicate/system/qbittorrent" = { - directories."${config.services.qbittorrent.mediaDir}" = { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = true; - write = false; - execute = true; - }; - }; - }; + config = lib.mkIf config.services.qbittorrent.enable { + storage.datasets.replicate = { + "system/root" = { + directories."${qbittorent_profile_directory}" = lib.mkIf config.services.qbittorrent.impermanence.enable { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + }; + }; + "system/qbittorrent" = { + directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + owner.permissions = { + read = true; + write = true; + execute = true; }; - } - ) - ])) - ]); + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = true; + write = false; + execute = true; + }; + }; + }; + }; + }; } diff --git a/modules/nixos-modules/server/radarr/storage.nix b/modules/nixos-modules/server/radarr/storage.nix index a9b4901..8f991c0 100644 --- a/modules/nixos-modules/server/radarr/storage.nix +++ b/modules/nixos-modules/server/radarr/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.radarr.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.radarr.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.radarr.dataDir == radarr_data_directory; - message = "radarr data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.radarr.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.radarr.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${radarr_data_directory}" = { - owner.name = "radarr"; - group.name = "radarr"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.radarr.enable { + storage.datasets.replicate."system/root" = { + directories."${radarr_data_directory}" = lib.mkIf config.services.radarr.impermanence.enable { + owner.name = "radarr"; + group.name = "radarr"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/reverseProxy/storage.nix b/modules/nixos-modules/server/reverseProxy/storage.nix index 29a3120..62b5451 100644 --- a/modules/nixos-modules/server/reverseProxy/storage.nix +++ b/modules/nixos-modules/server/reverseProxy/storage.nix @@ -10,19 +10,12 @@ in { default = config.services.reverseProxy.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.reverseProxy.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - (lib.mkIf (!config.services.reverseProxy.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.reverseProxy.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${dataDir}" = { - owner.name = "acme"; - group.name = "acme"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.reverseProxy.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.reverseProxy.impermanence.enable { + owner.name = "acme"; + group.name = "acme"; + }; + }; + }; } diff --git a/modules/nixos-modules/server/sonarr/storage.nix b/modules/nixos-modules/server/sonarr/storage.nix index aebd0a9..8587751 100644 --- a/modules/nixos-modules/server/sonarr/storage.nix +++ b/modules/nixos-modules/server/sonarr/storage.nix @@ -10,27 +10,12 @@ in { default = config.services.sonarr.enable && config.storage.impermanence.enable; }; - config = lib.mkIf config.services.sonarr.enable (lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.services.sonarr.dataDir == sonarr_data_directory; - message = "sonarr data directory does not match persistence"; - } - ]; - } - (lib.mkIf (!config.services.sonarr.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.sonarr.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${sonarr_data_directory}" = { - owner.name = "sonarr"; - group.name = "sonarr"; - }; - }; - }) - ])) - ]); + config = lib.mkIf config.services.sonarr.enable { + storage.datasets.replicate."system/root" = { + directories."${sonarr_data_directory}" = lib.mkIf config.services.sonarr.impermanence.enable { + owner.name = "sonarr"; + group.name = "sonarr"; + }; + }; + }; } diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix index dd70918..6fe8e5c 100644 --- a/modules/nixos-modules/ssh.nix +++ b/modules/nixos-modules/ssh.nix @@ -10,46 +10,35 @@ }; }; - config = lib.mkMerge [ - { - services = { - openssh = { - enable = true; - ports = [22]; - settings = { - PasswordAuthentication = false; - UseDns = true; - X11Forwarding = false; - }; + config = { + services = { + openssh = { + enable = true; + ports = [22]; + settings = { + PasswordAuthentication = false; + UseDns = true; + X11Forwarding = false; }; }; - } - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # SSH host keys need to be persisted to maintain server identity - } - (lib.mkIf (!config.services.openssh.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.openssh.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - files = builtins.listToAttrs ( - lib.lists.flatten ( - builtins.map (hostKey: [ - { - name = hostKey.path; - value = {enable = true;}; - } - { - name = "${hostKey.path}.pub"; - value = {enable = true;}; - } - ]) - config.services.openssh.hostKeys - ) - ); - }; - }) - ])) - ]; + }; + + storage.datasets.replicate."system/root" = { + files = lib.mkIf config.services.openssh.impermanence.enable (builtins.listToAttrs ( + lib.lists.flatten ( + builtins.map (hostKey: [ + { + name = hostKey.path; + value = {enable = true;}; + } + { + name = "${hostKey.path}.pub"; + value = {enable = true;}; + } + ]) + config.services.openssh.hostKeys + ) + )); + }; + }; } diff --git a/modules/nixos-modules/sync/storage.nix b/modules/nixos-modules/sync/storage.nix index 7532045..61bf855 100644 --- a/modules/nixos-modules/sync/storage.nix +++ b/modules/nixos-modules/sync/storage.nix @@ -13,45 +13,20 @@ in { }; }; - config = lib.mkIf config.services.syncthing.enable ( - lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # Syncthing needs persistent storage for configuration and data - } - (lib.mkIf (!config.services.syncthing.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.syncthing.impermanence.enable { - assertions = - [ - { - assertion = config.services.syncthing.configDir == configDir; - message = "syncthing config dir does not match persistence"; - } - ] - ++ lib.attrsets.mapAttrsToList (_: folder: { - assertion = lib.strings.hasPrefix mountDir folder.path; - message = "syncthing folder ${folder.label} is stored at ${folder.path} which not under the persisted path of ${mountDir}"; - }) - config.services.syncthing.settings.folders; - - storage.impermanence.datasets."persist/replicate/system/root" = { - directories = { - "${mountDir}" = { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - "${configDir}" = { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - }; - }; - }) - ])) - ] - ); + config = lib.mkIf config.services.syncthing.enable { + storage.datasets.replicate."system/root" = { + directories = { + "${mountDir}" = lib.mkIf config.services.syncthing.impermanence.enable { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + "${configDir}" = lib.mkIf config.services.syncthing.impermanence.enable { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + }; + }; + }; } diff --git a/modules/nixos-modules/tailscale/storage.nix b/modules/nixos-modules/tailscale/storage.nix index a417aaf..7ac7e9a 100644 --- a/modules/nixos-modules/tailscale/storage.nix +++ b/modules/nixos-modules/tailscale/storage.nix @@ -12,25 +12,13 @@ in { }; }; - config = lib.mkIf config.services.tailscale.enable ( - lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # Tailscale needs persistent storage for keys and configuration - } - (lib.mkIf (!config.services.tailscale.impermanence.enable) { - # TODO: placeholder to configure a unique dataset for this service - }) - (lib.mkIf config.services.tailscale.impermanence.enable { - storage.impermanence.datasets."persist/replicate/system/root" = { - directories."${tailscale_data_directory}" = { - enable = true; - owner.name = "root"; - group.name = "root"; - }; - }; - }) - ])) - ] - ); + config = lib.mkIf config.services.tailscale.enable { + storage.datasets.replicate."system/root" = { + directories."${tailscale_data_directory}" = lib.mkIf config.services.tailscale.impermanence.enable { + enable = true; + owner.name = "root"; + group.name = "root"; + }; + }; + }; } diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index ab123b9..4018db5 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -402,7 +402,7 @@ in { (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ { # sops age key needs to be available to pre persist for user generation - storage.zfs.datasets."persist/local/system/sops" = { + storage.datasets.local."system/sops" = { type = "zfs_fs"; mount = { enable = true; @@ -413,9 +413,9 @@ in { }; } (lib.mkIf (!config.storage.impermanence.enable) { - storage.zfs.datasets = lib.mkMerge ( + storage.datasets.replicate = lib.mkMerge ( builtins.map (user: { - "persist/replicate/home/${user.name}" = { + "home/${user.name}" = { type = "zfs_fs"; mount = { enable = true; @@ -428,9 +428,9 @@ in { ); }) (lib.mkIf config.storage.impermanence.enable { - storage.zfs.datasets = lib.mkMerge ( + storage.datasets.ephemeral = lib.mkMerge ( builtins.map (user: { - "ephemeral/home/${user.name}" = { + "home/${user.name}" = { type = "zfs_fs"; mount = { enable = true; From dfcacdc6fbcb028395b834972d36f4ca461c9fa7 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 16 Nov 2025 00:04:03 -0600 Subject: [PATCH 27/44] feat: moved some datasets to common zfs storage config --- modules/nixos-modules/storage/storage.nix | 58 ++++++++++++----------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 7d14dd7..1b85010 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -40,6 +40,37 @@ in { config = lib.mkMerge [ (lib.mkIf config.storage.zfs.enable { # Create ZFS datasets based on storage.datasets configuration + storage.datasets = { + local = { + "" = { + type = "zfs_fs"; + }; + "nix" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/nix"; + }; + snapshot = { + autoSnapshot = false; + }; + atime = "off"; + relatime = "off"; + }; + }; + replicate = { + "" = { + type = "zfs_fs"; + }; + "system/var/log" = { + type = "zfs_fs"; + mount = { + enable = true; + mountPoint = "/var/log"; + }; + }; + }; + }; }) (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable) { storage.datasets = { @@ -58,28 +89,7 @@ in { }; }; }; - local = { - "nix" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/nix"; - }; - snapshot = { - autoSnapshot = false; - }; - atime = "off"; - relatime = "off"; - }; - }; replicate = { - "system/var/log" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/var/log"; - }; - }; "system/root" = { mount = { enable = true; @@ -146,9 +156,6 @@ in { storage.datasets = { # Base organizational datasets (only needed when impermanence is disabled) local = { - "" = { - type = "zfs_fs"; - }; "root" = { type = "zfs_fs"; mount = { @@ -178,9 +185,6 @@ in { }; }; replicate = { - "" = { - type = "zfs_fs"; - }; "system/var/log" = { type = "zfs_fs"; mount = { From e196541f2a0544569709b046877e946add3a3ccd Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 16 Nov 2025 00:12:29 -0600 Subject: [PATCH 28/44] feat: filter out impermanence datasets that dont do anything --- modules/nixos-modules/storage/impermanence.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index db0deb0..8f6d6d8 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -109,7 +109,14 @@ in { }; }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); }) - config.storage.impermanence.datasets; + (lib.filterAttrs ( + datasetName: dataset: let + enabledDirectories = lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories; + enabledFiles = lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files; + in + (enabledDirectories != {}) || (enabledFiles != {}) + ) + config.storage.impermanence.datasets); } (lib.mkIf config.storage.zfs.enable { storage.zfs.datasets = From ecdd407abe8b5b3e2559977589fb935f112b1e37 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 17 Nov 2025 17:56:31 -0600 Subject: [PATCH 29/44] feat: switched jellyfin media and qbittorent media to being the same dataset --- modules/nixos-modules/server/jellyfin/storage.nix | 2 +- modules/nixos-modules/server/qbittorent/storage.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix index 98f7a8c..f6887b2 100644 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -27,7 +27,7 @@ in { }; }; }; - "system/jellyfin" = { + "system/media" = { atime = "off"; relatime = "off"; diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix index 8dabab8..edb9fb1 100644 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -18,7 +18,7 @@ in { group.name = "qbittorrent"; }; }; - "system/qbittorrent" = { + "system/media" = { directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { owner.name = "qbittorrent"; group.name = "qbittorrent"; From 29221143670076a629dc26c8a541d5b1a3c629d1 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 11:51:53 -0600 Subject: [PATCH 30/44] fix: fixed file system resolution --- modules/nixos-modules/storage/storage.nix | 72 +++++++------------ .../storage/submodules/dataset.nix | 14 ++-- .../submodules/impermanenceDataset.nix | 5 +- modules/nixos-modules/storage/zfs.nix | 4 +- modules/nixos-modules/users.nix | 15 +--- 5 files changed, 36 insertions(+), 74 deletions(-) diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 1b85010..6cff4f1 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -42,15 +42,9 @@ in { # Create ZFS datasets based on storage.datasets configuration storage.datasets = { local = { - "" = { - type = "zfs_fs"; - }; "nix" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/nix"; - }; + mount = "/nix"; snapshot = { autoSnapshot = false; }; @@ -59,15 +53,9 @@ in { }; }; replicate = { - "" = { - type = "zfs_fs"; - }; "system/var/log" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/var/log"; - }; + mount = "/var/log"; }; }; }; @@ -77,24 +65,29 @@ in { ephemeral = { "" = { type = "zfs_fs"; + mount = null; }; "system/root" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/"; - }; + mount = "/"; snapshot = { blankSnapshot = true; }; }; }; + # TODO: can we auto set the mount points on these to just be `"/persist/local/${name}"` + local = { + "" = { + mount = "/persist/local/"; + }; + }; + # TODO: can we auto set the mount points on these to just be `"/persist/replicate/${name}"` replicate = { + "" = { + mount = "/persist/replicate/"; + }; "system/root" = { - mount = { - enable = true; - mountPoint = "/persist/replicate/system/root"; - }; + mount = "/persist/replicate/system/root"; snapshot = { autoSnapshot = true; }; @@ -107,10 +100,7 @@ in { }; }; "home" = { - mount = { - enable = true; - mountPoint = "/persist/replicate/home"; - }; + mount = "/persist/replicate/home"; snapshot = { autoSnapshot = true; }; @@ -156,12 +146,13 @@ in { storage.datasets = { # Base organizational datasets (only needed when impermanence is disabled) local = { + "" = { + type = "zfs_fs"; + mount = ""; + }; "root" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/"; - }; + mount = "/"; compression = "lz4"; acltype = "posixacl"; relatime = "on"; @@ -171,26 +162,15 @@ in { blankSnapshot = true; }; }; - "nix" = { - type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/nix"; - }; - snapshot = { - autoSnapshot = false; - }; - atime = "off"; - relatime = "off"; - }; }; replicate = { + "" = { + type = "zfs_fs"; + mount = null; + }; "system/var/log" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/var/log"; - }; + mount = "/var/log"; }; }; }; diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix index 0b57886..2a45552 100644 --- a/modules/nixos-modules/storage/submodules/dataset.nix +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -42,16 +42,10 @@ description = "Synchronous write behavior"; }; - mount = { - enable = lib.mkOption { - type = lib.types.either lib.types.bool (lib.types.enum ["on" "off" "noauto"]); - default = true; - description = "Whether and how the dataset should be mounted"; - }; - mountPoint = lib.mkOption { - type = lib.types.str; - description = "Controls the mount point used for this file system"; - }; + mount = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "Controls the mount point used for this file system"; + default = null; }; encryption = { diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 0104b88..4090b48 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -47,9 +47,6 @@ in { }; config = { - mount = { - mountPoint = lib.mkDefault "/${name}"; - enable = lib.mkDefault true; - }; + mount = lib.mkDefault "/${name}"; }; } diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 1942e8d..1d3c1fb 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -83,7 +83,7 @@ args @ { lib.attrsets.nameValuePair name { type = dataset.type; options = datasetToZfsOptions dataset; - mountpoint = dataset.mount.mountPoint or null; + mountpoint = dataset.mount or null; postCreateHook = generatePostCreateHook name dataset; } ) @@ -92,7 +92,7 @@ args @ { lib.attrsets.nameValuePair "" { type = config.storage.zfs.rootDataset.type; options = datasetToZfsOptions config.storage.zfs.rootDataset; - mountpoint = config.storage.zfs.rootDataset.mount.mountPoint or null; + mountpoint = config.storage.zfs.rootDataset.mount or null; postCreateHook = generatePostCreateHook "" config.storage.zfs.rootDataset; } )) diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 4018db5..f952861 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -404,10 +404,7 @@ in { # sops age key needs to be available to pre persist for user generation storage.datasets.local."system/sops" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = SOPS_AGE_KEY_DIRECTORY; - }; + mount = SOPS_AGE_KEY_DIRECTORY; atime = "off"; relatime = "off"; }; @@ -417,10 +414,7 @@ in { builtins.map (user: { "home/${user.name}" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/home/${user.name}"; - }; + mount = "/home/${user.name}"; snapshot.autoSnapshot = true; }; }) @@ -432,10 +426,7 @@ in { builtins.map (user: { "home/${user.name}" = { type = "zfs_fs"; - mount = { - enable = true; - mountPoint = "/home/${user.name}"; - }; + mount = "/home/${user.name}"; snapshot.blankSnapshot = true; }; }) From a4f3b3141d59a1c73b56512317b644ae5a5df4d6 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 15:35:26 -0600 Subject: [PATCH 31/44] fix: fixed trailing mount path issue --- modules/nixos-modules/storage/impermanence.nix | 10 ++++++++-- modules/nixos-modules/storage/storage.nix | 6 ++++-- .../storage/submodules/impermanenceDataset.nix | 4 ++++ modules/nixos-modules/users.nix | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 8f6d6d8..9af5681 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -87,7 +87,10 @@ in { neededForBoot = true; } ) - config.storage.impermanence.datasets; + (lib.filterAttrs ( + datasetName: dataset: dataset.impermanence.enable + ) + config.storage.impermanence.datasets); environment.persistence = lib.mapAttrs (datasetName: dataset: { @@ -116,7 +119,10 @@ in { in (enabledDirectories != {}) || (enabledFiles != {}) ) - config.storage.impermanence.datasets); + (lib.filterAttrs ( + datasetName: dataset: dataset.impermanence.enable + ) + config.storage.impermanence.datasets)); } (lib.mkIf config.storage.zfs.enable { storage.zfs.datasets = diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 6cff4f1..c059733 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -43,6 +43,7 @@ in { storage.datasets = { local = { "nix" = { + impermanence.enable = false; type = "zfs_fs"; mount = "/nix"; snapshot = { @@ -54,6 +55,7 @@ in { }; replicate = { "system/var/log" = { + impermanence.enable = false; type = "zfs_fs"; mount = "/var/log"; }; @@ -78,13 +80,13 @@ in { # TODO: can we auto set the mount points on these to just be `"/persist/local/${name}"` local = { "" = { - mount = "/persist/local/"; + mount = "/persist/local"; }; }; # TODO: can we auto set the mount points on these to just be `"/persist/replicate/${name}"` replicate = { "" = { - mount = "/persist/replicate/"; + mount = "/persist/replicate"; }; "system/root" = { mount = "/persist/replicate/system/root"; diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix index 4090b48..e4d3584 100644 --- a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -44,6 +44,10 @@ in { type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); default = {}; }; + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; }; config = { diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index f952861..195d8b6 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -407,6 +407,7 @@ in { mount = SOPS_AGE_KEY_DIRECTORY; atime = "off"; relatime = "off"; + impermanence.enable = false; }; } (lib.mkIf (!config.storage.impermanence.enable) { From 3d1750060de1669ce93a1d546ad537848b628ddc Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 15:44:59 -0600 Subject: [PATCH 32/44] fix: fixed nix flake check --- modules/nixos-modules/server/jellyfin/storage.nix | 3 +-- modules/nixos-modules/server/qbittorent/storage.nix | 2 ++ modules/nixos-modules/storage/storage.nix | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix index f6887b2..5cff3e8 100644 --- a/modules/nixos-modules/server/jellyfin/storage.nix +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -28,8 +28,7 @@ in { }; }; "system/media" = { - atime = "off"; - relatime = "off"; + mount = "/persist/replicate/system/media"; directories."${config.services.jellyfin.media_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { enable = true; diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix index edb9fb1..da82bcc 100644 --- a/modules/nixos-modules/server/qbittorent/storage.nix +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -19,6 +19,8 @@ in { }; }; "system/media" = { + mount = "/persist/replicate/system/media"; + directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { owner.name = "qbittorrent"; group.name = "qbittorrent"; diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index c059733..2247559 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -150,7 +150,7 @@ in { local = { "" = { type = "zfs_fs"; - mount = ""; + mount = null; }; "root" = { type = "zfs_fs"; From a0807b014c087a03dc585076ff455152af7de82f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Thu, 27 Nov 2025 20:29:03 -0600 Subject: [PATCH 33/44] feat: moved logs to impermanence --- modules/nixos-modules/storage/storage.nix | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 2247559..5f9f6f1 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -53,13 +53,6 @@ in { relatime = "off"; }; }; - replicate = { - "system/var/log" = { - impermanence.enable = false; - type = "zfs_fs"; - mount = "/var/log"; - }; - }; }; }) (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable) { @@ -107,6 +100,12 @@ in { autoSnapshot = true; }; }; + "system/var/log" = { + type = "zfs_fs"; + directories = { + "/var/log".enable = true; + }; + }; }; }; From 8060e39b1193608d93821c6a27c098000053e7ac Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 30 Nov 2025 13:46:25 -0600 Subject: [PATCH 34/44] feat: updated android studio config to match new patter --- .../programs/android-studio.nix | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/modules/home-manager-modules/programs/android-studio.nix b/modules/home-manager-modules/programs/android-studio.nix index 0f76276..7c60e6e 100644 --- a/modules/home-manager-modules/programs/android-studio.nix +++ b/modules/home-manager-modules/programs/android-studio.nix @@ -14,22 +14,17 @@ android-studio ]; } - # TODO: create this - # ( - # lib.mkIf config.impermanence.enable { - # home.persistence."/persist${config.home.homeDirectory}" = { - # directories = [ - # # configuration - # "${config.xdg.configHome}/Google/AndroidStudio" - # # Android SDK - # ".android" - # # Gradle cache - # ".gradle" - # # Android Studio projects cache - # "${config.xdg.cacheHome}/Google/AndroidStudio" - # ]; - # }; - # } - # ) + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist/replicate/home" = { + directories = [ + "${config.xdg.configHome}/Google/AndroidStudio" + ".android" + ".gradle" + "${config.xdg.cacheHome}/Google/AndroidStudio" + ]; + }; + } + ) ]); } From e6e53141ce83ef5be528a6eec797ee89334dc338 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Thu, 15 Jan 2026 20:09:21 -0600 Subject: [PATCH 35/44] feat: switched back to main for impermanence --- flake.lock | 11 +++++------ flake.nix | 2 +- util/default.nix | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index fc9272e..0c32755 100644 --- a/flake.lock +++ b/flake.lock @@ -138,16 +138,15 @@ ] }, "locked": { - "lastModified": 1762761176, - "narHash": "sha256-i3gM8fUozQrgZIbwVNlTuhLqPSl56zxAYpsQpQ9Lhro=", - "owner": "jan-leila", + "lastModified": 1767822991, + "narHash": "sha256-iyrn9AcPZCoyxX4OT8eMkBsjG7SRUQXXS/V1JzxS7rA=", + "owner": "nix-community", "repo": "impermanence", - "rev": "ffbe1ca47cf4b3008c3aa5c49cdae294d8c8058a", + "rev": "82e5bc4508cab9e8d5a136626276eb5bbce5e9c5", "type": "github" }, "original": { - "owner": "jan-leila", - "ref": "home-manager-v2", + "owner": "nix-community", "repo": "impermanence", "type": "github" } diff --git a/flake.nix b/flake.nix index 5fe65db..df5f6e9 100644 --- a/flake.nix +++ b/flake.nix @@ -36,7 +36,7 @@ # delete your darlings impermanence = { - url = "github:jan-leila/impermanence/home-manager-v2"; + url = "github:nix-community/impermanence"; inputs.nixpkgs.follows = "nixpkgs"; inputs.home-manager.follows = "home-manager"; }; diff --git a/util/default.nix b/util/default.nix index 246543d..d72d00d 100644 --- a/util/default.nix +++ b/util/default.nix @@ -29,7 +29,6 @@ common-modules ++ [ sops-nix.homeManagerModules.sops - impermanence.homeManagerModules.impermanence ../modules/home-manager-modules ]; From 3370cd7ab305db5f87be1a19b4e12230e39bab81 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Thu, 15 Jan 2026 21:50:36 -0600 Subject: [PATCH 36/44] feat: bound impermanence filesystem datasets filesystem --- modules/nixos-modules/storage/impermanence.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 9af5681..cb20295 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -84,6 +84,8 @@ in { lib.mapAttrs' ( datasetName: dataset: lib.nameValuePair "/${datasetName}" { + device = "rpool/${datasetName}"; + fsType = "zfs"; neededForBoot = true; } ) From 18c738cc2f0d59c104ca4b73a8df4db5d56df65e Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 8 Feb 2026 12:37:42 -0600 Subject: [PATCH 37/44] feat: disabled impermanence for all the needed services --- .../nixos/defiant/configuration.nix | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 9aa2e34..52eb452 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -226,6 +226,7 @@ postgresql = { enable = true; adminUsers = ["leyla"]; + impermanence.enable = false; }; # temp enable desktop environment for setup @@ -244,6 +245,7 @@ reverseProxy = { enable = true; openFirewall = true; + impermanence.enable = false; acme = { enable = true; email = "jan-leila@protonmail.com"; @@ -253,6 +255,7 @@ ollama = { enable = true; exposePort = true; + impermanence.enable = false; environmentVariables = { OLLAMA_KEEP_ALIVE = "24h"; @@ -287,6 +290,7 @@ enable = true; authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; useRoutingFeatures = "server"; + impermanence.enable = false; extraUpFlags = [ "--advertise-exit-node" "--advertise-routes=192.168.0.0/24" @@ -299,24 +303,33 @@ ]; }; - syncthing.enable = true; + syncthing = { + enable = true; + impermanence.enable = false; + }; - fail2ban.enable = true; + fail2ban = { + enable = true; + impermanence.enable = false; + }; jellyfin = { enable = true; domain = "media.jan-leila.com"; extraDomains = ["jellyfin.jan-leila.com"]; + impermanence.enable = false; }; immich = { enable = true; domain = "photos.jan-leila.com"; + impermanence.enable = false; }; forgejo = { enable = true; reverseProxy.domain = "git.jan-leila.com"; + impermanence.enable = false; }; searx = { @@ -327,6 +340,7 @@ actual = { enable = true; domain = "budget.jan-leila.com"; + impermanence.enable = false; }; home-assistant = { @@ -334,6 +348,7 @@ domain = "home.jan-leila.com"; openFirewall = true; postgres.enable = true; + impermanence.enable = false; extensions = { sonos.enable = true; @@ -346,11 +361,13 @@ enable = true; domain = "documents.jan-leila.com"; passwordFile = config.sops.secrets."services/paperless_password".path; + impermanence.enable = false; }; panoramax = { enable = false; openFirewall = true; + impermanence.enable = false; }; crab-hole = { @@ -358,6 +375,7 @@ port = 8085; openFirewall = true; show_doc = true; + impermanence.enable = false; downstreams = { host = { enable = true; @@ -373,31 +391,38 @@ mediaDir = "/srv/qbittorent"; openFirewall = true; webuiPort = 8084; + impermanence.enable = false; }; sonarr = { enable = true; openFirewall = true; + impermanence.enable = false; }; radarr = { enable = true; openFirewall = true; + impermanence.enable = false; }; bazarr = { enable = true; openFirewall = true; + impermanence.enable = false; }; lidarr = { enable = true; openFirewall = true; + impermanence.enable = false; }; jackett = { enable = true; openFirewall = true; + impermanence.enable = false; }; flaresolverr = { enable = true; openFirewall = true; + impermanence.enable = false; }; }; From 3302af38b38ab61ea9de9f065ac213da3d8d2e58 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 8 Feb 2026 12:50:58 -0600 Subject: [PATCH 38/44] feat: moved legacy datasets from main into defiant configuration --- configurations/nixos/defiant/default.nix | 1 + .../nixos/defiant/legacy-impermanence.nix | 214 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 configurations/nixos/defiant/legacy-impermanence.nix diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix index 3013946..d53f9cc 100644 --- a/configurations/nixos/defiant/default.nix +++ b/configurations/nixos/defiant/default.nix @@ -4,5 +4,6 @@ ./hardware-configuration.nix ./configuration.nix ./packages.nix + ./legacy-impermanence.nix ]; } diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix new file mode 100644 index 0000000..5d6081c --- /dev/null +++ b/configurations/nixos/defiant/legacy-impermanence.nix @@ -0,0 +1,214 @@ +# Legacy impermanence module for defiant +# This module contains all the impermanence configurations that were previously +# handled by individual service modules on the main branch. It allows us to +# merge the storage-refactor branch into main while keeping current functionality, +# and then migrate services one at a time to the new automated impermanence system. +# +# To migrate a service to the new system: +# 1. Remove the service's configuration from this file +# 2. Set `impermanence.enable = true` for that service in configuration.nix +# 3. Remove `impermanence.enable = false` from the service configuration +{ + config, + lib, + ... +}: { + config = lib.mkIf config.storage.impermanence.enable { + environment.persistence."/persist/replicate/system/root" = { + enable = true; + hideMounts = true; + directories = lib.mkMerge [ + # PostgreSQL + (lib.mkIf config.services.postgresql.enable [ + { + directory = "/var/lib/postgresql/16"; + user = "postgres"; + group = "postgres"; + } + ]) + + # Reverse Proxy (ACME) + (lib.mkIf config.services.reverseProxy.enable [ + { + directory = "/var/lib/acme"; + user = "acme"; + group = "acme"; + } + ]) + + # Ollama + (lib.mkIf config.services.ollama.enable [ + { + directory = "/var/lib/private/ollama"; + user = config.services.ollama.user; + group = config.services.ollama.group; + mode = "0700"; + } + ]) + + # Tailscale + (lib.mkIf config.services.tailscale.enable [ + { + directory = "/var/lib/tailscale"; + user = "root"; + group = "root"; + } + ]) + + # Syncthing + (lib.mkIf config.services.syncthing.enable [ + { + directory = "/mnt/sync"; + user = "syncthing"; + group = "syncthing"; + } + { + directory = "/etc/syncthing"; + user = "syncthing"; + group = "syncthing"; + } + ]) + + # Fail2ban + (lib.mkIf config.services.fail2ban.enable [ + { + directory = "/var/lib/fail2ban"; + user = "fail2ban"; + group = "fail2ban"; + } + ]) + + # Jellyfin + (lib.mkIf config.services.jellyfin.enable [ + { + directory = "/var/lib/jellyfin"; + user = "jellyfin"; + group = "jellyfin"; + } + { + directory = "/var/cache/jellyfin"; + user = "jellyfin"; + group = "jellyfin"; + } + ]) + + # Immich + (lib.mkIf config.services.immich.enable [ + { + directory = "/var/lib/immich"; + user = "immich"; + group = "immich"; + } + ]) + + # Forgejo + (lib.mkIf config.services.forgejo.enable [ + { + directory = "/var/lib/forgejo"; + user = "forgejo"; + group = "forgejo"; + } + ]) + + # Actual + (lib.mkIf config.services.actual.enable [ + { + directory = "/var/lib/private/actual"; + user = "actual"; + group = "actual"; + } + ]) + + # Home Assistant + (lib.mkIf config.services.home-assistant.enable [ + { + directory = "/var/lib/hass"; + user = "hass"; + group = "hass"; + } + ]) + + # Paperless + (lib.mkIf config.services.paperless.enable [ + { + directory = "/var/lib/paperless"; + user = "paperless"; + group = "paperless"; + } + ]) + + # Crab-hole + (lib.mkIf config.services.crab-hole.enable [ + { + directory = "/var/lib/private/crab-hole"; + user = "crab-hole"; + group = "crab-hole"; + } + ]) + + # qBittorrent + (lib.mkIf config.services.qbittorrent.enable [ + { + directory = "/var/lib/qBittorrent/"; + user = "qbittorrent"; + group = "qbittorrent"; + } + ]) + + # Sonarr + (lib.mkIf config.services.sonarr.enable [ + { + directory = "/var/lib/sonarr/.config/NzbDrone"; + user = "sonarr"; + group = "sonarr"; + } + ]) + + # Radarr + (lib.mkIf config.services.radarr.enable [ + { + directory = "/var/lib/radarr/.config/Radarr"; + user = "radarr"; + group = "radarr"; + } + ]) + + # Bazarr + (lib.mkIf config.services.bazarr.enable [ + { + directory = "/var/lib/bazarr"; + user = "bazarr"; + group = "bazarr"; + } + ]) + + # Lidarr + (lib.mkIf config.services.lidarr.enable [ + { + directory = "/var/lib/lidarr/.config/Lidarr"; + user = "lidarr"; + group = "lidarr"; + } + ]) + + # Jackett + (lib.mkIf config.services.jackett.enable [ + { + directory = "/var/lib/jackett/.config/Jackett"; + user = "jackett"; + group = "jackett"; + } + ]) + + # FlareSolverr + (lib.mkIf config.services.flaresolverr.enable [ + { + directory = "/var/lib/flaresolverr"; + user = "flaresolverr"; + group = "flaresolverr"; + } + ]) + ]; + }; + }; +} From 6ce567a53b7df1cdfb1a54907b1addd297efda47 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 8 Feb 2026 13:03:05 -0600 Subject: [PATCH 39/44] fix: added missing impermanence configs --- configurations/nixos/defiant/legacy-impermanence.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix index 5d6081c..27d0813 100644 --- a/configurations/nixos/defiant/legacy-impermanence.nix +++ b/configurations/nixos/defiant/legacy-impermanence.nix @@ -90,6 +90,12 @@ user = "jellyfin"; group = "jellyfin"; } + { + directory = config.services.jellyfin.media_directory; + user = "jellyfin"; + group = "jellyfin_media"; + mode = "1770"; + } ]) # Immich @@ -153,6 +159,12 @@ user = "qbittorrent"; group = "qbittorrent"; } + { + directory = config.services.qbittorrent.mediaDir; + user = "qbittorrent"; + group = "qbittorrent"; + mode = "1775"; + } ]) # Sonarr From 65e0c6e0e5e8c22de2e2eeadb4c92a9fc7361ac7 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 8 Feb 2026 18:01:31 -0600 Subject: [PATCH 40/44] fix: added missing datasets to config --- .../nixos/defiant/configuration.nix | 1 + configurations/nixos/defiant/default.nix | 1 + .../nixos/defiant/legacy-impermanence.nix | 65 ++++++++--- .../nixos/defiant/legacy-storage.nix | 103 ++++++++++++++++++ flake.lock | 14 +-- .../nixos-modules/storage/impermanence.nix | 7 +- modules/nixos-modules/storage/storage.nix | 36 +++--- 7 files changed, 185 insertions(+), 42 deletions(-) create mode 100644 configurations/nixos/defiant/legacy-storage.nix diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 52eb452..390ae71 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -67,6 +67,7 @@ }; storage = { + generateBase = false; zfs = { enable = true; notifications = { diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix index d53f9cc..dd2383f 100644 --- a/configurations/nixos/defiant/default.nix +++ b/configurations/nixos/defiant/default.nix @@ -4,6 +4,7 @@ ./hardware-configuration.nix ./configuration.nix ./packages.nix + ./legacy-storage.nix ./legacy-impermanence.nix ]; } diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix index 27d0813..b272fb8 100644 --- a/configurations/nixos/defiant/legacy-impermanence.nix +++ b/configurations/nixos/defiant/legacy-impermanence.nix @@ -14,7 +14,17 @@ ... }: { config = lib.mkIf config.storage.impermanence.enable { - environment.persistence."/persist/replicate/system/root" = { + system.activationScripts = { + "var-lib-private-permissions" = { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/system/root/var/lib/private + chmod 0700 /persist/system/root/var/lib/private + ''; + }; + }; + + environment.persistence."/persist/system/root" = { enable = true; hideMounts = true; directories = lib.mkMerge [ @@ -78,7 +88,7 @@ } ]) - # Jellyfin + # Jellyfin (data/cache only - media is on separate dataset) (lib.mkIf config.services.jellyfin.enable [ { directory = "/var/lib/jellyfin"; @@ -90,12 +100,6 @@ user = "jellyfin"; group = "jellyfin"; } - { - directory = config.services.jellyfin.media_directory; - user = "jellyfin"; - group = "jellyfin_media"; - mode = "1770"; - } ]) # Immich @@ -152,19 +156,13 @@ } ]) - # qBittorrent + # qBittorrent (config only - media is on separate dataset) (lib.mkIf config.services.qbittorrent.enable [ { directory = "/var/lib/qBittorrent/"; user = "qbittorrent"; group = "qbittorrent"; } - { - directory = config.services.qbittorrent.mediaDir; - user = "qbittorrent"; - group = "qbittorrent"; - mode = "1775"; - } ]) # Sonarr @@ -222,5 +220,42 @@ ]) ]; }; + + # Jellyfin media on separate dataset (matching main) + environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.jellyfin.media_directory; + user = "jellyfin"; + group = "jellyfin_media"; + mode = "1770"; + } + ]; + }; + + # qBittorrent media on separate dataset (matching main) + environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.qbittorrent.mediaDir; + user = "qbittorrent"; + group = "qbittorrent"; + mode = "1775"; + } + ]; + }; + + # /var/log persistence (matching main) + environment.persistence."/persist/system/var/log" = { + enable = true; + hideMounts = true; + directories = [ + "/var/log" + ]; + }; }; } diff --git a/configurations/nixos/defiant/legacy-storage.nix b/configurations/nixos/defiant/legacy-storage.nix new file mode 100644 index 0000000..b998e2c --- /dev/null +++ b/configurations/nixos/defiant/legacy-storage.nix @@ -0,0 +1,103 @@ +# Legacy storage configuration for defiant +# This file manually defines ZFS datasets matching the main branch structure +# to allow incremental migration to the new storage module. +# +# Datasets from main branch: +# - local/ - ephemeral parent +# - local/home/leyla - ephemeral user home +# - local/system/nix - nix store +# - local/system/root - root filesystem (rolled back on boot) +# - local/system/sops - sops age key +# - persist/ - persistent parent +# - persist/home/leyla - persistent user home +# - persist/system/jellyfin - jellyfin media +# - persist/system/qbittorrent - qbittorrent media +# - persist/system/root - persistent root data +# - persist/system/var/log - log persistence +{lib, ...}: { + # Manually define ZFS datasets matching main's structure + storage.zfs.datasets = { + # Ephemeral datasets (local/) + "local" = { + type = "zfs_fs"; + mount = null; + }; + "local/home/leyla" = { + type = "zfs_fs"; + mount = "/home/leyla"; + snapshot = { + blankSnapshot = true; + }; + }; + "local/system/nix" = { + type = "zfs_fs"; + mount = "/nix"; + atime = "off"; + relatime = "off"; + snapshot = { + autoSnapshot = false; + }; + }; + "local/system/root" = { + type = "zfs_fs"; + mount = "/"; + snapshot = { + blankSnapshot = true; + }; + }; + "local/system/sops" = { + type = "zfs_fs"; + mount = "/persist/sops"; + }; + + # Persistent datasets (persist/) + "persist" = { + type = "zfs_fs"; + mount = null; + }; + "persist/home/leyla" = { + type = "zfs_fs"; + mount = "/persist/home/leyla"; + snapshot = { + autoSnapshot = true; + }; + }; + "persist/system/jellyfin" = { + type = "zfs_fs"; + mount = "/persist/system/jellyfin"; + atime = "off"; + relatime = "off"; + }; + "persist/system/qbittorrent" = { + type = "zfs_fs"; + mount = "/persist/system/qbittorrent"; + atime = "off"; + relatime = "off"; + }; + "persist/system/root" = { + type = "zfs_fs"; + mount = "/persist/system/root"; + snapshot = { + autoSnapshot = true; + }; + }; + "persist/system/var/log" = { + type = "zfs_fs"; + mount = "/persist/system/var/log"; + }; + }; + + # Boot commands to rollback ephemeral root on boot + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/local/system/root@blank + ''; + + # FileSystems needed for boot + fileSystems = { + "/".neededForBoot = true; + "/persist/system/root".neededForBoot = true; + "/persist/system/var/log".neededForBoot = true; + "/persist/system/jellyfin".neededForBoot = true; + "/persist/system/qbittorrent".neededForBoot = true; + }; +} diff --git a/flake.lock b/flake.lock index 0c32755..6116658 100644 --- a/flake.lock +++ b/flake.lock @@ -129,20 +129,12 @@ } }, "impermanence": { - "inputs": { - "home-manager": [ - "home-manager" - ], - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1767822991, - "narHash": "sha256-iyrn9AcPZCoyxX4OT8eMkBsjG7SRUQXXS/V1JzxS7rA=", + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", "owner": "nix-community", "repo": "impermanence", - "rev": "82e5bc4508cab9e8d5a136626276eb5bbce5e9c5", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", "type": "github" }, "original": { diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index cb20295..637e882 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -66,10 +66,11 @@ in { } ]; - # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 - system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; system.activationScripts = { - "var-lib-private-permissions" = { + # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 + "createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; + + "var-lib-private-permissions" = lib.mkIf config.storage.generateBase { deps = ["specialfs"]; text = '' mkdir -p /persist/replicate/system/root/var/lib/private diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index 5f9f6f1..a0b4fc9 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -22,23 +22,33 @@ args @ { # Find options that are only in impermanence datasets (not in regular ZFS datasets) impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; in { - options.storage.datasets = { - ephemeral = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); - default = {}; + options.storage = { + generateBase = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + When enabled, enables automatic generation of base datasets (ephemeral, local, replicate roots). + This allows manual definition of datasets matching an existing system layout for migration purposes. + ''; }; - local = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); - default = {}; - }; - replicate = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); - default = {}; + datasets = { + ephemeral = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + default = {}; + }; + local = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + replicate = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; }; }; config = lib.mkMerge [ - (lib.mkIf config.storage.zfs.enable { + (lib.mkIf (config.storage.zfs.enable && config.storage.generateBase) { # Create ZFS datasets based on storage.datasets configuration storage.datasets = { local = { @@ -55,7 +65,7 @@ in { }; }; }) - (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable) { + (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable && config.storage.generateBase) { storage.datasets = { ephemeral = { "" = { From fa0adaa51145653b7bbce6404cd54f2d62cc50e9 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 7 Mar 2026 11:15:45 -0600 Subject: [PATCH 41/44] feat: reenabled auto snapshot/scrubbing --- modules/nixos-modules/storage/zfs.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 1d3c1fb..0d6ca18 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -212,10 +212,10 @@ in { # in # diskWarnings ++ datasetWarnings; - # services.zfs = { - # autoScrub.enable = true; - # autoSnapshot.enable = true; - # }; + services.zfs = { + autoScrub.enable = true; + autoSnapshot.enable = true; + }; # # Configure disko for ZFS setup disko.devices = { From 1145703cfe30700ed12cbda894e5b3fda361fcff Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 7 Mar 2026 12:03:09 -0600 Subject: [PATCH 42/44] feat: fixed merge incompatibilities --- configurations/home-manager/eve/packages.nix | 2 +- .../home-manager/leyla/packages/default.nix | 2 +- .../nixos/defiant/configuration.nix | 1 - .../nixos/defiant/legacy-impermanence.nix | 97 ++++++++---- .../nixos/defiant/legacy-storage.nix | 147 ++++++++++++++++-- flake.lock | 125 ++++++--------- modules/home-manager-modules/impermanence.nix | 7 +- .../home-manager-modules/programs/signal.nix | 6 +- .../nixos-modules/storage/impermanence.nix | 3 + 9 files changed, 259 insertions(+), 131 deletions(-) diff --git a/configurations/home-manager/eve/packages.nix b/configurations/home-manager/eve/packages.nix index 6b3c2e2..ac24fa2 100644 --- a/configurations/home-manager/eve/packages.nix +++ b/configurations/home-manager/eve/packages.nix @@ -60,7 +60,7 @@ in { bitwarden.enable = true; discord.enable = true; makemkv.enable = true; - signal-desktop-bin.enable = true; + signal-desktop.enable = true; steam.enable = true; piper.enable = hardware.piperMouse.enable; krita.enable = true; diff --git a/configurations/home-manager/leyla/packages/default.nix b/configurations/home-manager/leyla/packages/default.nix index 475269d..5f64742 100644 --- a/configurations/home-manager/leyla/packages/default.nix +++ b/configurations/home-manager/leyla/packages/default.nix @@ -50,7 +50,7 @@ in { android-studio.enable = true; makemkv.enable = true; discord.enable = true; - signal-desktop-bin.enable = true; + signal-desktop.enable = true; calibre.enable = true; obsidian.enable = true; jetbrains.idea-oss.enable = true; diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 5885264..40adbd5 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -67,7 +67,6 @@ }; storage = { - generateBase = false; zfs = { enable = true; notifications = { diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix index b272fb8..4cfe18b 100644 --- a/configurations/nixos/defiant/legacy-impermanence.nix +++ b/configurations/nixos/defiant/legacy-impermanence.nix @@ -1,19 +1,32 @@ # Legacy impermanence module for defiant -# This module contains all the impermanence configurations that were previously -# handled by individual service modules on the main branch. It allows us to -# merge the storage-refactor branch into main while keeping current functionality, -# and then migrate services one at a time to the new automated impermanence system. +# See legacy-storage.nix for the full incremental migration plan. # -# To migrate a service to the new system: -# 1. Remove the service's configuration from this file -# 2. Set `impermanence.enable = true` for that service in configuration.nix -# 3. Remove `impermanence.enable = false` from the service configuration +# This file is consumed in two phases: +# +# Phase 3 (after generateBase is enabled): +# Remove the SYSTEM-LEVEL entries marked [PHASE 3] below. These will be +# handled automatically by storage.nix, ssh.nix, and the impermanence module: +# - var-lib-private-permissions activation script +# - /etc/machine-id +# - SSH host keys +# - /var/lib/nixos +# - /var/lib/systemd/coredump +# - /persist/system/var/log persistence block +# +# Phase 4 (migrate services one at a time, any order): +# For each service: +# 1. Remove the service's section marked [PHASE 4] from this file +# 2. Remove `impermanence.enable = false` for that service in configuration.nix +# For jellyfin/qbittorrent, also remove the separate media persistence blocks. +# +# Phase 5: Delete this file once empty. { config, lib, ... }: { config = lib.mkIf config.storage.impermanence.enable { + # [PHASE 3] Remove this activation script after enabling generateBase system.activationScripts = { "var-lib-private-permissions" = { deps = ["specialfs"]; @@ -27,8 +40,28 @@ environment.persistence."/persist/system/root" = { enable = true; hideMounts = true; + # [PHASE 3] Remove this files block after enabling generateBase + files = lib.mkMerge [ + ["/etc/machine-id"] + # SSH host keys + (lib.mkIf config.services.openssh.enable ( + lib.lists.flatten ( + builtins.map (hostKey: [ + hostKey.path + "${hostKey.path}.pub" + ]) + config.services.openssh.hostKeys + ) + )) + ]; directories = lib.mkMerge [ - # PostgreSQL + # [PHASE 3] Remove these system directories after enabling generateBase + [ + "/var/lib/nixos" + "/var/lib/systemd/coredump" + ] + + # [PHASE 4] PostgreSQL (lib.mkIf config.services.postgresql.enable [ { directory = "/var/lib/postgresql/16"; @@ -37,7 +70,7 @@ } ]) - # Reverse Proxy (ACME) + # [PHASE 4] Reverse Proxy (ACME) (lib.mkIf config.services.reverseProxy.enable [ { directory = "/var/lib/acme"; @@ -46,7 +79,7 @@ } ]) - # Ollama + # [PHASE 4] Ollama (lib.mkIf config.services.ollama.enable [ { directory = "/var/lib/private/ollama"; @@ -56,7 +89,7 @@ } ]) - # Tailscale + # [PHASE 4] Tailscale (lib.mkIf config.services.tailscale.enable [ { directory = "/var/lib/tailscale"; @@ -65,7 +98,7 @@ } ]) - # Syncthing + # [PHASE 4] Syncthing (lib.mkIf config.services.syncthing.enable [ { directory = "/mnt/sync"; @@ -79,7 +112,7 @@ } ]) - # Fail2ban + # [PHASE 4] Fail2ban (lib.mkIf config.services.fail2ban.enable [ { directory = "/var/lib/fail2ban"; @@ -88,7 +121,7 @@ } ]) - # Jellyfin (data/cache only - media is on separate dataset) + # [PHASE 4] Jellyfin (data/cache only - media is on separate dataset) (lib.mkIf config.services.jellyfin.enable [ { directory = "/var/lib/jellyfin"; @@ -102,7 +135,7 @@ } ]) - # Immich + # [PHASE 4] Immich (lib.mkIf config.services.immich.enable [ { directory = "/var/lib/immich"; @@ -111,7 +144,7 @@ } ]) - # Forgejo + # [PHASE 4] Forgejo (lib.mkIf config.services.forgejo.enable [ { directory = "/var/lib/forgejo"; @@ -120,7 +153,7 @@ } ]) - # Actual + # [PHASE 4] Actual (lib.mkIf config.services.actual.enable [ { directory = "/var/lib/private/actual"; @@ -129,7 +162,7 @@ } ]) - # Home Assistant + # [PHASE 4] Home Assistant (lib.mkIf config.services.home-assistant.enable [ { directory = "/var/lib/hass"; @@ -138,7 +171,7 @@ } ]) - # Paperless + # [PHASE 4] Paperless (lib.mkIf config.services.paperless.enable [ { directory = "/var/lib/paperless"; @@ -147,7 +180,7 @@ } ]) - # Crab-hole + # [PHASE 4] Crab-hole (lib.mkIf config.services.crab-hole.enable [ { directory = "/var/lib/private/crab-hole"; @@ -156,7 +189,7 @@ } ]) - # qBittorrent (config only - media is on separate dataset) + # [PHASE 4] qBittorrent (config only - media is on separate dataset) (lib.mkIf config.services.qbittorrent.enable [ { directory = "/var/lib/qBittorrent/"; @@ -165,7 +198,7 @@ } ]) - # Sonarr + # [PHASE 4] Sonarr (lib.mkIf config.services.sonarr.enable [ { directory = "/var/lib/sonarr/.config/NzbDrone"; @@ -174,7 +207,7 @@ } ]) - # Radarr + # [PHASE 4] Radarr (lib.mkIf config.services.radarr.enable [ { directory = "/var/lib/radarr/.config/Radarr"; @@ -183,7 +216,7 @@ } ]) - # Bazarr + # [PHASE 4] Bazarr (lib.mkIf config.services.bazarr.enable [ { directory = "/var/lib/bazarr"; @@ -192,7 +225,7 @@ } ]) - # Lidarr + # [PHASE 4] Lidarr (lib.mkIf config.services.lidarr.enable [ { directory = "/var/lib/lidarr/.config/Lidarr"; @@ -201,7 +234,7 @@ } ]) - # Jackett + # [PHASE 4] Jackett (lib.mkIf config.services.jackett.enable [ { directory = "/var/lib/jackett/.config/Jackett"; @@ -210,7 +243,7 @@ } ]) - # FlareSolverr + # [PHASE 4] FlareSolverr (lib.mkIf config.services.flaresolverr.enable [ { directory = "/var/lib/flaresolverr"; @@ -221,7 +254,8 @@ ]; }; - # Jellyfin media on separate dataset (matching main) + # [PHASE 4 - LAST] Jellyfin media on separate dataset + # Requires Phase 2 media dataset merge before migrating (several days of data copy) environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable { enable = true; hideMounts = true; @@ -235,7 +269,8 @@ ]; }; - # qBittorrent media on separate dataset (matching main) + # [PHASE 4 - LAST] qBittorrent media on separate dataset + # Requires Phase 2 media dataset merge before migrating (several days of data copy) environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable { enable = true; hideMounts = true; @@ -249,7 +284,7 @@ ]; }; - # /var/log persistence (matching main) + # [PHASE 3] /var/log persistence - handled by storage.nix after generateBase environment.persistence."/persist/system/var/log" = { enable = true; hideMounts = true; diff --git a/configurations/nixos/defiant/legacy-storage.nix b/configurations/nixos/defiant/legacy-storage.nix index b998e2c..9ab79a6 100644 --- a/configurations/nixos/defiant/legacy-storage.nix +++ b/configurations/nixos/defiant/legacy-storage.nix @@ -1,20 +1,131 @@ # Legacy storage configuration for defiant -# This file manually defines ZFS datasets matching the main branch structure -# to allow incremental migration to the new storage module. +# This file manually defines ZFS datasets matching the existing on-disk layout +# to allow incremental migration to the new storage module (generateBase = true). # -# Datasets from main branch: -# - local/ - ephemeral parent -# - local/home/leyla - ephemeral user home -# - local/system/nix - nix store -# - local/system/root - root filesystem (rolled back on boot) -# - local/system/sops - sops age key -# - persist/ - persistent parent -# - persist/home/leyla - persistent user home -# - persist/system/jellyfin - jellyfin media -# - persist/system/qbittorrent - qbittorrent media -# - persist/system/root - persistent root data -# - persist/system/var/log - log persistence +# ============================================================================ +# INCREMENTAL MIGRATION PLAN +# ============================================================================ +# +# Current disk usage (for reference): +# rpool/local/system/nix ~26G (renamed in place, no copy) +# rpool/local/system/sops ~328K (renamed in place, no copy) +# rpool/persist/system/jellyfin ~32T (renamed in place, no copy) +# rpool/persist/system/qbittorrent ~6.5T (copied into media dataset, ~6.5T temp) +# rpool free space ~30T +# +# Phase 1: Migrate base datasets on disk (boot from live USB or rescue) +# All operations in this phase are instant renames -- no data is copied. +# +# Unlock the pool: +# zfs load-key -a +# +# Step 1a: Move nix and sops out of local/ (they go to persist/local/) +# The -p flag auto-creates the parent datasets. +# +# zfs rename -p rpool/local/system/nix rpool/persist/local/nix +# zfs rename -p rpool/local/system/sops rpool/persist/local/system/sops +# +# Step 1b: Rename local/ -> ephemeral/ (takes remaining children with it) +# zfs rename rpool/local rpool/ephemeral +# # This moves: local/system/root -> ephemeral/system/root +# # local/home/leyla -> ephemeral/home/leyla +# +# Step 1c: Recreate blank snapshots on ephemeral datasets +# zfs destroy rpool/ephemeral/system/root@blank +# zfs snapshot rpool/ephemeral/system/root@blank +# zfs destroy rpool/ephemeral/home/leyla@blank +# zfs snapshot rpool/ephemeral/home/leyla@blank +# +# Step 1d: Move persist/ children under persist/replicate/ +# zfs create -o canmount=off rpool/persist/replicate +# zfs create -o canmount=off rpool/persist/replicate/system +# zfs rename rpool/persist/system/root rpool/persist/replicate/system/root +# zfs rename rpool/persist/system/var rpool/persist/replicate/system/var +# zfs rename rpool/persist/home/leyla rpool/persist/replicate/home +# # Clean up the now-empty home parent +# zfs destroy rpool/persist/home +# # NOTE: Do NOT destroy rpool/persist/system -- it still contains +# # persist/system/jellyfin and persist/system/qbittorrent which are +# # migrated in Phase 2. +# +# Verify the new layout: +# zfs list -r rpool -o name,used,mountpoint +# +# Phase 2: Merge media into a single dataset (do this last) +# Strategy: Rename the jellyfin dataset to become the shared media dataset +# (zero copy, instant), then copy qbittorrent data into it (~6.5T copy). +# This avoids duplicating the 32T jellyfin dataset. +# +# Step 2a: Rename jellyfin dataset to the shared media name +# zfs rename rpool/persist/system/jellyfin rpool/persist/replicate/system/media +# +# Step 2b: Copy qbittorrent data into the media dataset +# This copies ~6.5T and may take several hours/days depending on disk speed. +# The qbittorrent data is not critical to back up so no snapshot needed. +# +# systemctl stop qbittorrent +# rsync -avPHAX /persist/system/qbittorrent/ /persist/replicate/system/media/ +# +# Step 2c: Verify the data and clean up +# ls -la /persist/replicate/system/media/ +# zfs destroy rpool/persist/system/qbittorrent +# # persist/system should now be empty, clean it up: +# zfs destroy rpool/persist/system +# +# Phase 3: Enable generateBase +# In the nix config: +# - Delete this file (legacy-storage.nix) and remove its import from default.nix +# - Remove [PHASE 3] entries from legacy-impermanence.nix: +# - var-lib-private-permissions activation script +# - /etc/machine-id, SSH host keys (files block) +# - /var/lib/nixos, /var/lib/systemd/coredump (directories) +# - /persist/system/var/log persistence block +# These are now handled automatically by storage.nix and ssh.nix. +# Rebuild and verify: +# sudo nixos-rebuild switch --flake .#defiant +# # Verify mounts: findmnt -t fuse.bindfs,fuse +# # Verify persist: ls /persist/replicate/system/root/var/lib/nixos +# # Verify boot: reboot and confirm system comes up cleanly +# +# Phase 4: Migrate services (one at a time, any order) +# For each service (except jellyfin/qbittorrent): +# 1. Remove the service's [PHASE 4] section from legacy-impermanence.nix +# 2. Remove `impermanence.enable = false` for that service in configuration.nix +# 3. Rebuild: sudo nixos-rebuild switch --flake .#defiant +# 4. Verify: systemctl status , check the service's data is intact +# No data migration is needed -- the data already lives on the renamed +# dataset at the new path. +# +# Migrate jellyfin and qbittorrent LAST (after Phase 2 media merge): +# 1. Remove [PHASE 4 - LAST] jellyfin entries from legacy-impermanence.nix +# 2. Remove [PHASE 4 - LAST] qbittorrent entries from legacy-impermanence.nix +# 3. Remove `impermanence.enable = false` for both in configuration.nix +# 4. Rebuild: sudo nixos-rebuild switch --flake .#defiant +# 5. Verify: systemctl status jellyfin qbittorrent +# +# Phase 5: Cleanup +# Once all services are migrated and legacy-impermanence.nix is empty: +# - Delete legacy-impermanence.nix and remove its import from default.nix +# - Rebuild: sudo nixos-rebuild switch --flake .#defiant +# +# ============================================================================ +# +# Current on-disk dataset layout: +# rpool/local/ - ephemeral parent +# rpool/local/home/leyla - ephemeral user home (rolled back on boot) +# rpool/local/system/nix - nix store +# rpool/local/system/root - root filesystem (rolled back on boot) +# rpool/local/system/sops - sops age key +# rpool/persist/ - persistent parent +# rpool/persist/home/leyla - persistent user home +# rpool/persist/system/jellyfin - jellyfin media +# rpool/persist/system/qbittorrent - qbittorrent media +# rpool/persist/system/root - persistent root data +# rpool/persist/system/var/log - log persistence {lib, ...}: { + # Disable automatic base dataset generation so we can define them manually + storage.generateBase = false; + # Manually define ZFS datasets matching main's structure storage.zfs.datasets = { # Ephemeral datasets (local/) @@ -47,7 +158,7 @@ }; "local/system/sops" = { type = "zfs_fs"; - mount = "/persist/sops"; + mount = "/var/lib/sops-nix"; }; # Persistent datasets (persist/) @@ -87,9 +198,10 @@ }; }; - # Boot commands to rollback ephemeral root on boot + # Boot commands to rollback ephemeral root and user homes on boot boot.initrd.postResumeCommands = lib.mkAfter '' zfs rollback -r rpool/local/system/root@blank + zfs rollback -r rpool/local/home/leyla@blank ''; # FileSystems needed for boot @@ -99,5 +211,8 @@ "/persist/system/var/log".neededForBoot = true; "/persist/system/jellyfin".neededForBoot = true; "/persist/system/qbittorrent".neededForBoot = true; + "/var/lib/sops-nix".neededForBoot = true; + "/persist/home/leyla".neededForBoot = true; + "/home/leyla".neededForBoot = true; }; } diff --git a/flake.lock b/flake.lock index 1403bb4..14c8561 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1771881364, - "narHash": "sha256-A5uE/hMium5of/QGC6JwF5TGoDAfpNtW00T0s9u/PN8=", + "lastModified": 1772867152, + "narHash": "sha256-RIFgZ4O6Eg+5ysZ8Tqb3YvcqiRaNy440GEY22ltjRrs=", "owner": "nix-community", "repo": "disko", - "rev": "a4cb7bf73f264d40560ba527f9280469f1f081c6", + "rev": "eaafb89b56e948661d618eefd4757d9ea8d77514", "type": "github" }, "original": { @@ -28,11 +28,11 @@ }, "locked": { "dir": "pkgs/firefox-addons", - "lastModified": 1771888219, - "narHash": "sha256-XlA/l99y1Qilmd8ttYJ9y5BSse9GKoQlt9hnY8H+EHM=", + "lastModified": 1772856163, + "narHash": "sha256-xD+d1+FVhKJ+oFYMTWOdVSBoXS4yeMyVZyDjMXqWEJE=", "owner": "rycee", "repo": "nur-expressions", - "rev": "a347c1da78da64eeb78a0c9005bdaadace33e83c", + "rev": "d358a550c7beac5f04fbc5a786e14af079606689", "type": "gitlab" }, "original": { @@ -115,32 +115,11 @@ ] }, "locked": { - "lastModified": 1771851181, - "narHash": "sha256-gFgE6mGUftwseV3DUENMb0k0EiHd739lZexPo5O/sdQ=", + "lastModified": 1772845525, + "narHash": "sha256-Dp5Ir2u4jJDGCgeMRviHvEQDe+U37hMxp6RSNOoMMPc=", "owner": "nix-community", "repo": "home-manager", - "rev": "9a4b494b1aa1b93d8edf167f46dc8e0c0011280c", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "home-manager", - "type": "github" - } - }, - "home-manager_2": { - "inputs": { - "nixpkgs": [ - "impermanence", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1768598210, - "narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=", - "owner": "nix-community", - "repo": "home-manager", - "rev": "c47b2cc64a629f8e075de52e4742de688f930dc6", + "rev": "27b93804fbef1544cb07718d3f0a451f4c4cd6c0", "type": "github" }, "original": { @@ -150,12 +129,20 @@ } }, "impermanence": { + "inputs": { + "home-manager": [ + "home-manager" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1737831083, - "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "lastModified": 1769548169, + "narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=", "owner": "nix-community", "repo": "impermanence", - "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "rev": "7b1d382faf603b6d264f58627330f9faa5cba149", "type": "github" }, "original": { @@ -204,14 +191,14 @@ "mcp-nixos": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1769804089, - "narHash": "sha256-Wkot1j0cTx64xxjmLXzPubTckaZBSUJFhESEdOzPYas=", + "lastModified": 1772769318, + "narHash": "sha256-RAyOW5JMXRhiREqxFPOzw80fVsYVBnOPFgBSjnJ6gbY=", "owner": "utensils", "repo": "mcp-nixos", - "rev": "37a691ea4ea9c8bdcccfe174c6127847b8213fd3", + "rev": "60c1efbba0de1268b42f1144c904e6c8a9627dde", "type": "github" }, "original": { @@ -227,11 +214,11 @@ ] }, "locked": { - "lastModified": 1771520882, - "narHash": "sha256-9SeTZ4Pwr730YfT7V8Azb8GFbwk1ZwiQDAwft3qAD+o=", + "lastModified": 1772379624, + "narHash": "sha256-NG9LLTWlz4YiaTAiRGChbrzbVxBfX+Auq4Ab/SWmk4A=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "6a7fdcd5839ec8b135821179eea3b58092171bcf", + "rev": "52d061516108769656a8bd9c6e811c677ec5b462", "type": "github" }, "original": { @@ -268,11 +255,11 @@ ] }, "locked": { - "lastModified": 1771901087, - "narHash": "sha256-b5eSke+C8UeR5Er+TZOzHCDStBJ68yyFlqAUc6fNBX0=", + "lastModified": 1772850876, + "narHash": "sha256-Ga19zlfMpakCY4GMwBSOljNLOF0nEYrYBXv0hP/d4rw=", "owner": "nix-community", "repo": "nix-vscode-extensions", - "rev": "c22e7adea9adec98b3dc79be954ee17d56a232bd", + "rev": "22f084d4c280dfc8a9d764f7b85af38e5d69c3dc", "type": "github" }, "original": { @@ -283,11 +270,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1771423359, - "narHash": "sha256-yRKJ7gpVmXbX2ZcA8nFi6CMPkJXZGjie2unsiMzj3Ig=", + "lastModified": 1771969195, + "narHash": "sha256-qwcDBtrRvJbrrnv1lf/pREQi8t2hWZxVAyeMo7/E9sw=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "740a22363033e9f1bb6270fbfb5a9574067af15b", + "rev": "41c6b421bdc301b2624486e11905c9af7b8ec68e", "type": "github" }, "original": { @@ -299,15 +286,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1768564909, - "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", - "owner": "nixos", + "lastModified": 1767640445, + "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", + "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", "type": "github" }, "original": { - "owner": "nixos", + "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -330,37 +317,21 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1767640445, - "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", - "owner": "NixOS", + "lastModified": 1772773019, + "narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", + "rev": "aca4d95fce4914b3892661bcb80b8087293536c6", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_3": { - "locked": { - "lastModified": 1771369470, - "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "0182a361324364ae3f436a63005877674cf45efb", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { "locked": { "lastModified": 1759070547, "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", @@ -378,7 +349,7 @@ }, "noita-entangled-worlds": { "inputs": { - "nixpkgs": "nixpkgs_4", + "nixpkgs": "nixpkgs_3", "rust-overlay": "rust-overlay", "systems": "systems_2" }, @@ -410,7 +381,7 @@ "nix-syncthing": "nix-syncthing", "nix-vscode-extensions": "nix-vscode-extensions", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_2", "noita-entangled-worlds": "noita-entangled-worlds", "secrets": "secrets", "sops-nix": "sops-nix" @@ -460,11 +431,11 @@ ] }, "locked": { - "lastModified": 1771889317, - "narHash": "sha256-YV17Q5lEU0S9ppw08Y+cs4eEQJBuc79AzblFoHORLMU=", + "lastModified": 1772495394, + "narHash": "sha256-hmIvE/slLKEFKNEJz27IZ8BKlAaZDcjIHmkZ7GCEjfw=", "owner": "Mic92", "repo": "sops-nix", - "rev": "b027513c32e5b39b59f64626b87fbe168ae02094", + "rev": "1d9b98a29a45abe9c4d3174bd36de9f28755e3ff", "type": "github" }, "original": { diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index f5e9869..fcc130d 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -26,8 +26,13 @@ in { # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."/persist/replicate/home" = { + home.persistence."${ + if osConfig.storage.generateBase + then "/persist/replicate/home" + else "/persist/home/${config.home.username}" + }" = { directories = ["."]; + allowOther = true; }; }) ]; diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index 962a139..bf5205e 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -4,14 +4,14 @@ config, ... }: { - options.programs.signal-desktop-bin = { + options.programs.signal-desktop = { enable = lib.mkEnableOption "enable signal"; }; - config = lib.mkIf config.programs.signal-desktop-bin.enable (lib.mkMerge [ + config = lib.mkIf config.programs.signal-desktop.enable (lib.mkMerge [ { home.packages = with pkgs; [ - signal-desktop-bin + signal-desktop ]; } ( diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix index 637e882..4fdf803 100644 --- a/modules/nixos-modules/storage/impermanence.nix +++ b/modules/nixos-modules/storage/impermanence.nix @@ -81,6 +81,9 @@ in { programs.fuse.userAllowOther = true; + # Suppress sudo lecture on every boot since impermanence wipes the lecture status file + security.sudo.extraConfig = "Defaults lecture=never"; + fileSystems = lib.mapAttrs' ( datasetName: dataset: From 2f7bbf3e1c8a8d7aa5a34aedd5ee033cef58efc4 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 7 Mar 2026 14:33:08 -0600 Subject: [PATCH 43/44] feat: fixed more missing datasets --- configurations/nixos/emergent/default.nix | 1 + .../nixos/emergent/legacy-storage.nix | 51 +++++++++++++++++++ modules/nixos-modules/storage/storage.nix | 2 +- modules/nixos-modules/storage/zfs.nix | 8 ++- modules/nixos-modules/users.nix | 7 ++- 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 configurations/nixos/emergent/legacy-storage.nix diff --git a/configurations/nixos/emergent/default.nix b/configurations/nixos/emergent/default.nix index 452334a..3acaeda 100644 --- a/configurations/nixos/emergent/default.nix +++ b/configurations/nixos/emergent/default.nix @@ -3,5 +3,6 @@ imports = [ ./configuration.nix ./hardware-configuration.nix + ./legacy-storage.nix ]; } diff --git a/configurations/nixos/emergent/legacy-storage.nix b/configurations/nixos/emergent/legacy-storage.nix new file mode 100644 index 0000000..2b24729 --- /dev/null +++ b/configurations/nixos/emergent/legacy-storage.nix @@ -0,0 +1,51 @@ +# Legacy storage configuration for emergent +# This file manually defines ZFS datasets matching the existing on-disk layout +# to allow incremental migration to the new storage module (generateBase = true). +# +# Current on-disk dataset layout: +# rpool/local/ - parent (canmount=off) +# rpool/local/system/nix - nix store +# rpool/local/system/root - root filesystem +# +# Migration plan: +# Phase 1: Rename datasets on disk (boot from live USB) +# zfs rename -p rpool/local/system/nix rpool/persist/local/nix +# zfs rename rpool/local rpool/persist/local +# # This moves: local/system/root -> persist/local/root (need to rename after) +# # Actually, since local/system/root needs to become persist/local/root: +# zfs rename rpool/persist/local/system/root rpool/persist/local/root +# zfs destroy rpool/persist/local/system # now empty +# # Recreate blank snapshot: +# zfs destroy rpool/persist/local/root@blank +# zfs snapshot rpool/persist/local/root@blank +# +# Phase 2: Delete this file, remove its import from default.nix, rebuild. +{...}: { + # Disable automatic base dataset generation so we can define them manually + storage.generateBase = false; + + # Manually define ZFS datasets matching the existing on-disk layout + storage.zfs.datasets = { + "local" = { + type = "zfs_fs"; + mount = null; + }; + "local/system/nix" = { + type = "zfs_fs"; + mount = "/nix"; + atime = "off"; + relatime = "off"; + snapshot = { + autoSnapshot = false; + }; + }; + "local/system/root" = { + type = "zfs_fs"; + mount = "/"; + snapshot = { + blankSnapshot = true; + autoSnapshot = true; + }; + }; + }; +} diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix index a0b4fc9..771d661 100644 --- a/modules/nixos-modules/storage/storage.nix +++ b/modules/nixos-modules/storage/storage.nix @@ -153,7 +153,7 @@ in { config.storage.datasets.replicate) ]; }) - (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable) { + (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable && config.storage.generateBase) { storage.datasets = { # Base organizational datasets (only needed when impermanence is disabled) local = { diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix index 0d6ca18..2fc6cb4 100644 --- a/modules/nixos-modules/storage/zfs.nix +++ b/modules/nixos-modules/storage/zfs.nix @@ -9,6 +9,12 @@ args @ { # Hash function for disk names (max 27 chars to fit GPT limitations) hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + # Map "stripe" to "" for disko compatibility (disko uses "" for stripe mode) + diskoPoolMode = + if config.storage.zfs.pool.mode == "stripe" + then "" + else config.storage.zfs.pool.mode; + # Helper to flatten vdevs into list of devices with names allVdevDevices = lib.lists.flatten (builtins.map ( vdev: @@ -260,7 +266,7 @@ in { type = "topology"; vdev = builtins.map (vdev: { - mode = config.storage.zfs.pool.mode; + mode = diskoPoolMode; members = builtins.map (device: hashDisk device.device) vdev; }) config.storage.zfs.pool.vdevs; diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 8a384e3..9cef952 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -409,10 +409,13 @@ in { ); # Post resume commands to rollback user home datasets to blank snapshots - boot.initrd.postResumeCommands = lib.mkAfter ( + # Only add these when generateBase is true -- when false, the legacy + # storage config is responsible for providing rollback commands with + # the correct (old) dataset paths. + boot.initrd.postResumeCommands = lib.mkIf config.storage.generateBase (lib.mkAfter ( lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") normalUsers) - ); + )); # TODO: I don't think we need this anymore but I have not tested it # Create persist home directories with proper permissions From 16089e0371c2d4fed5f463baf9653c57211220e0 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 7 Mar 2026 19:00:01 -0600 Subject: [PATCH 44/44] fix: fixed more datasets --- configurations/home-manager/leyla/impermanence.nix | 2 +- modules/home-manager-modules/impermanence.nix | 14 +++++++++----- modules/home-manager-modules/openssh.nix | 2 +- .../programs/android-studio.nix | 2 +- modules/home-manager-modules/programs/anki.nix | 2 +- .../home-manager-modules/programs/bitwarden.nix | 2 +- modules/home-manager-modules/programs/bruno.nix | 2 +- modules/home-manager-modules/programs/calibre.nix | 2 +- .../programs/davinci-resolve.nix | 2 +- modules/home-manager-modules/programs/dbeaver.nix | 2 +- modules/home-manager-modules/programs/discord.nix | 2 +- modules/home-manager-modules/programs/firefox.nix | 2 +- modules/home-manager-modules/programs/freecad.nix | 2 +- modules/home-manager-modules/programs/gimp.nix | 2 +- modules/home-manager-modules/programs/idea.nix | 2 +- modules/home-manager-modules/programs/inkscape.nix | 2 +- modules/home-manager-modules/programs/kdenlive.nix | 2 +- modules/home-manager-modules/programs/krita.nix | 2 +- .../home-manager-modules/programs/libreoffice.nix | 2 +- modules/home-manager-modules/programs/makemkv.nix | 2 +- .../programs/mapillary-uploader.nix | 2 +- modules/home-manager-modules/programs/obs.nix | 2 +- modules/home-manager-modules/programs/obsidian.nix | 2 +- modules/home-manager-modules/programs/olympus.nix | 2 +- modules/home-manager-modules/programs/openrgb.nix | 2 +- modules/home-manager-modules/programs/picard.nix | 2 +- .../programs/prostudiomasters.nix | 2 +- .../home-manager-modules/programs/protonvpn.nix | 2 +- .../home-manager-modules/programs/qbittorrent.nix | 2 +- modules/home-manager-modules/programs/qflipper.nix | 2 +- modules/home-manager-modules/programs/signal.nix | 2 +- modules/home-manager-modules/programs/steam.nix | 2 +- .../home-manager-modules/programs/tor-browser.nix | 2 +- .../programs/ungoogled-chromium.nix | 2 +- modules/home-manager-modules/programs/via.nix | 2 +- .../programs/vmware-workstation.nix | 2 +- 36 files changed, 44 insertions(+), 40 deletions(-) diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index 4a58cbb..8fbff41 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.impermanence.enable) { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "desktop" "downloads" diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix index fcc130d..e8b3ec4 100644 --- a/modules/home-manager-modules/impermanence.nix +++ b/modules/home-manager-modules/impermanence.nix @@ -12,6 +12,14 @@ in { type = lib.types.bool; default = true; }; + persistencePath = lib.mkOption { + type = lib.types.str; + default = + if osConfig.storage.generateBase + then "/persist/replicate/home" + else "/persist"; + description = "The base path for user home persistence. The impermanence module will automatically append the user's home directory path. Automatically adapts based on whether the system uses the new dataset layout or the legacy one."; + }; }; config = lib.mkMerge [ @@ -26,11 +34,7 @@ in { # If impermanence is not enabled for this user but system impermanence is enabled, # persist the entire home directory as fallback (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."${ - if osConfig.storage.generateBase - then "/persist/replicate/home" - else "/persist/home/${config.home.username}" - }" = { + home.persistence."${cfg.persistencePath}" = { directories = ["."]; allowOther = true; }; diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix index cbe7d8d..2f44957 100644 --- a/modules/home-manager-modules/openssh.nix +++ b/modules/home-manager-modules/openssh.nix @@ -96,7 +96,7 @@ } ) (lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { files = lib.lists.flatten ( builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys ); diff --git a/modules/home-manager-modules/programs/android-studio.nix b/modules/home-manager-modules/programs/android-studio.nix index 7c60e6e..8d1e28c 100644 --- a/modules/home-manager-modules/programs/android-studio.nix +++ b/modules/home-manager-modules/programs/android-studio.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/Google/AndroidStudio" ".android" diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix index c54feac..dcabce8 100644 --- a/modules/home-manager-modules/programs/anki.nix +++ b/modules/home-manager-modules/programs/anki.nix @@ -4,7 +4,7 @@ ... }: { config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ ".local/share/Anki2" ]; diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index ade24b6..bbd2086 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/Bitwarden" ]; diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index ced1998..7bc64b6 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/bruno/" ]; diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index f41ced7..7174b43 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -12,7 +12,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/calibre" ]; diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix index c17c8b0..5956578 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.dataHome}/DaVinciResolve" "${config.xdg.configHome}/blackmagic" diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index f509646..1595a02 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.dataHome}/DBeaverData/" ]; diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index 0b0588e..e42367b 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/discord/" ]; diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix index e100200..2756e31 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -25,7 +25,7 @@ }; in { config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { - home.persistence."/persist/replicate/home" = lib.mkMerge ( + home.persistence."${config.impermanence.persistencePath}" = lib.mkMerge ( ( lib.attrsets.mapAttrsToList (profile: _: buildProfilePersistence profile) diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index 19e08fa..50600db 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/FreeCAD" ]; diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index fbe4471..95c87e6 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/GIMP" ]; diff --git a/modules/home-manager-modules/programs/idea.nix b/modules/home-manager-modules/programs/idea.nix index b195096..a1aebda 100644 --- a/modules/home-manager-modules/programs/idea.nix +++ b/modules/home-manager-modules/programs/idea.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ # configuration "${config.xdg.configHome}/JetBrains/" diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index 67e5f80..28eb334 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/inkscape" ]; diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix index 2bec5b3..2c4bac8 100644 --- a/modules/home-manager-modules/programs/kdenlive.nix +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/kdenliverc" "${config.xdg.dataHome}/kdenlive" diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index 88d1de9..dd7bb12 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/kritarc" "${config.xdg.dataHome}/krita" diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index 9c3537f..283c8db 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/libreoffice" ]; diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix index 9fcde8b..f748f68 100644 --- a/modules/home-manager-modules/programs/makemkv.nix +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -30,7 +30,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ ".MakeMKV" ]; diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index 09894c9..0d9ad5f 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -17,7 +17,7 @@ in { } ( mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/mapillary-uploader" "${config.xdg.dataHome}/mapillary-uploader" diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index 3a099f7..0a4caf7 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/obs-studio" ]; diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix index e07beab..6676ecd 100644 --- a/modules/home-manager-modules/programs/obsidian.nix +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -6,7 +6,7 @@ config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/obsidian" ]; diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix index 3223d62..2d5adb6 100644 --- a/modules/home-manager-modules/programs/olympus.nix +++ b/modules/home-manager-modules/programs/olympus.nix @@ -23,7 +23,7 @@ in { } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/olympus" "${config.xdg.dataHome}/olympus" diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index 64d6229..c350b1e 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/OpenRGB" ]; diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index 5d197f8..ffc4289 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/MusicBrainz" ]; diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix index 5256f26..d61b7e5 100644 --- a/modules/home-manager-modules/programs/prostudiomasters.nix +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/ProStudioMasters" ]; diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix index 57e50ab..5742948 100644 --- a/modules/home-manager-modules/programs/protonvpn.nix +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/protonvpn" "${config.xdg.configHome}/Proton" diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix index ee098e0..b2e0f50 100644 --- a/modules/home-manager-modules/programs/qbittorrent.nix +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/qBittorrent" ]; diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index 0c7d242..bb141a4 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/qFlipper" ]; diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index bf5205e..a50a49e 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/Signal" ]; diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index 3dd6504..4e0644e 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -18,7 +18,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ { directory = "${config.xdg.dataHome}/Steam"; diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index 92484ae..c108805 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.dataHome}/torbrowser" ]; diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix index e76eeeb..32f4b40 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/chromium" ]; diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index 3a638aa..ad6f45a 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -16,7 +16,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ "${config.xdg.configHome}/via" "${config.xdg.dataHome}/via" diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix index 277e4bd..76f260b 100644 --- a/modules/home-manager-modules/programs/vmware-workstation.nix +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -17,7 +17,7 @@ } ( lib.mkIf config.impermanence.enable { - home.persistence."/persist/replicate/home" = { + home.persistence."${config.impermanence.persistencePath}" = { directories = [ { directory = ".vmware";