From 78dd22fed3939862ea1e2714177060a6d143ce5a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 3 Nov 2025 11:55:10 -0600 Subject: [PATCH 01/32] 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"; -- 2.51.0 From 573708fd479ea551a83c54d120f22b9ca5ae6496 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Tue, 4 Nov 2025 15:02:49 -0600 Subject: [PATCH 02/32] 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); + }; + }; + }; +} -- 2.51.0 From 2fd14e4cc0c4c8f8a05ed91a67e9d868c67dd783 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Tue, 4 Nov 2025 19:39:27 -0600 Subject: [PATCH 03/32] 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; + }; + }; + }; + }) + ]); } -- 2.51.0 From d8989bb43d3353aaf2e11b01d3ea797502612436 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Wed, 5 Nov 2025 10:56:04 -0600 Subject: [PATCH 04/32] 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 = { -- 2.51.0 From adc6b90c93dd402ec2fb4c77d94bfa4fc8ab5f33 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 7 Nov 2025 16:29:56 -0600 Subject: [PATCH 05/32] 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 -- 2.51.0 From 0de97fa4a2869bbce5954ee6ab21867b232a231a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 7 Nov 2025 18:14:00 -0600 Subject: [PATCH 06/32] 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; + }; }; }; -- 2.51.0 From 9df29cc07f5592e3c61e924856ceb872f30db2b2 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 13:21:01 -0600 Subject: [PATCH 07/32] 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 = { -- 2.51.0 From 39edb655390fc1f6ca43c28082a0171abf33ea3a Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 14:21:22 -0600 Subject: [PATCH 08/32] 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 = { -- 2.51.0 From 3ca0e9bf0a3166741c634b0e6d0ba300769d9317 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 17:04:53 -0600 Subject: [PATCH 09/32] 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 = { -- 2.51.0 From b67be1472a1b4552b57b1672129899ef8475dd41 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:17:22 -0600 Subject: [PATCH 10/32] 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"; + }; + }; + }) + ])) + ]); +} -- 2.51.0 From d283f881604027bb6f51d0ac8556a8cb7e394c37 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:28:34 -0600 Subject: [PATCH 11/32] 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 + } + ] + ); +} -- 2.51.0 From ab555f50ff118a4f90720723671ad45c0c44b91e Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:30:49 -0600 Subject: [PATCH 12/32] 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 = { -- 2.51.0 From 703530ddfe22c61f475a1f9a3ada43264993fa2c Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:48:41 -0600 Subject: [PATCH 13/32] 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 = []; }; }; }; -- 2.51.0 From 5acf060e9e9e874acfe7cdecd75e9ff67afa53b5 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 18:49:19 -0600 Subject: [PATCH 14/32] 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 -- 2.51.0 From 1310b50794cc8261ea129695814f270ce8df056b Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 19:04:59 -0600 Subject: [PATCH 15/32] 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 + ) + ); + }; + }) + ])) ]; } -- 2.51.0 From 4da5d65d8f81e4add37ed0ddda014515e324f29f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 21:10:18 -0600 Subject: [PATCH 16/32] 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 -- 2.51.0 From 4d7d11e0c886ebb0a7e252f30eb57cf6fdc9b7f1 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 21:19:54 -0600 Subject: [PATCH 17/32] 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"; - } - ) - ]; -} -- 2.51.0 From 318a0a974884ce803d7bd5517c56692b822f9a56 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 8 Nov 2025 22:37:19 -0600 Subject: [PATCH 18/32] 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 + # ) + # ); + # }) ]; } -- 2.51.0 From d06c25f33f07e198d00da6331ed26cf470deb9ff Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 02:38:28 -0600 Subject: [PATCH 19/32] 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 - # ) - # ); - # }) ]; } -- 2.51.0 From 61eef3067e36c187b4615b2ad9d1d7101fe4027f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:42:25 -0600 Subject: [PATCH 20/32] 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"; -- 2.51.0 From 8aa984a389b949ca4e0fad20c32af931341b2083 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:49:12 -0600 Subject: [PATCH 21/32] 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 )) -- 2.51.0 From ac0f1ce2e69c3da050f44eb1ed93e54b213de353 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 10 Nov 2025 15:51:28 -0600 Subject: [PATCH 22/32] 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 = { -- 2.51.0 From 1eb66d1c31e5dbd8471aa2652b0db11115389444 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Wed, 12 Nov 2025 19:27:12 -0600 Subject: [PATCH 23/32] 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; }; -- 2.51.0 From f8edad75bf526a819a698a6df813bcecc2c06b4f Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Fri, 14 Nov 2025 22:06:32 -0600 Subject: [PATCH 24/32] 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; + # }; + }) ])) ]; } -- 2.51.0 From 757a3892e12f58ab078341c19e1d6172ef318e3b Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 15 Nov 2025 13:39:53 -0600 Subject: [PATCH 25/32] 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"; -- 2.51.0 From c2701ea8f0605cb8744ab53bf206a756685bc396 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sat, 15 Nov 2025 16:37:10 -0600 Subject: [PATCH 26/32] 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; -- 2.51.0 From dfcacdc6fbcb028395b834972d36f4ca461c9fa7 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 16 Nov 2025 00:04:03 -0600 Subject: [PATCH 27/32] 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 = { -- 2.51.0 From e196541f2a0544569709b046877e946add3a3ccd Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 16 Nov 2025 00:12:29 -0600 Subject: [PATCH 28/32] 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 = -- 2.51.0 From ecdd407abe8b5b3e2559977589fb935f112b1e37 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Mon, 17 Nov 2025 17:56:31 -0600 Subject: [PATCH 29/32] 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"; -- 2.51.0 From 29221143670076a629dc26c8a541d5b1a3c629d1 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 11:51:53 -0600 Subject: [PATCH 30/32] 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; }; }) -- 2.51.0 From a4f3b3141d59a1c73b56512317b644ae5a5df4d6 Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 15:35:26 -0600 Subject: [PATCH 31/32] 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) { -- 2.51.0 From 3d1750060de1669ce93a1d546ad537848b628ddc Mon Sep 17 00:00:00 2001 From: Leyla Becker Date: Sun, 23 Nov 2025 15:44:59 -0600 Subject: [PATCH 32/32] 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"; -- 2.51.0