commit 444c8af1e46fcd52b2018256631a02349e38d735
parent 96fd5cba7081c68b18736c75065009ea3f458c53
Author: Katja (zaphyra) <git@ctu.cx>
Date: Mon, 2 Jun 2025 08:25:52 +0200
parent 96fd5cba7081c68b18736c75065009ea3f458c53
Author: Katja (zaphyra) <git@ctu.cx>
Date: Mon, 2 Jun 2025 08:25:52 +0200
config/nixos/modules/filesystem/rootDisk: implement `zfs` support
1 file changed, 323 insertions(+), 198 deletions(-)
M
|
521
+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
diff --git a/config/nixos/modules/filesystem/rootDisk.nix b/config/nixos/modules/filesystem/rootDisk.nix @@ -1,5 +1,6 @@ { povSelf, + hostConfig, config, lib, pkgs, @@ -9,6 +10,11 @@ let inherit (lib) types; cfg = lib.getAttrFromPath povSelf config; + users = ( + config.modules.users + |> lib.mapAttrsToList (name: value: if value.enable then name else null) + |> lib.filter (element: !builtins.isNull element) + ); part = name: content: if cfg.encrypt then @@ -28,8 +34,8 @@ in }; type = { type = types.enum [ - "btrfs" "zfs" + "btrfs" "ext4" ]; }; @@ -40,6 +46,32 @@ in type = types.bool; default = false; }; + parts = { + home = { + type = types.bool; + default = false; + }; + homePerUser = { + type = types.bool; + default = cfg.parts.home; + }; + nix = { + type = types.bool; + default = true; + }; + tmp = { + type = types.bool; + default = false; + }; + system = { + type = types.bool; + default = false; + }; + }; + reservedSpace = { + type = types.str; + default = "8G"; + }; swap = { enable = { type = types.bool; @@ -53,222 +85,315 @@ in config = lib.mkIf cfg.enable ( lib.mkMerge [ - (lib.mkIf (cfg.type == "btrfs") { - services.btrfs.autoScrub = { - enable = true; - interval = "weekly"; - }; - }) - (lib.mkIf (cfg.type == "zfs") { - services.zfs.autoScrub.enable = true; + ( + lib.mkIf (cfg.type == "zfs") { + assertions = [ + { + assertion = cfg.parts.homePerUser -> cfg.parts.home; + message = "Option ${cfg}.parts.homePerUser requires ${cfg}.parts.home to be true."; + } + ]; - boot = lib.mkIf cfg.encrypt { - initrd = { - luks.forceLuksSupportInInitrd = true; - supportedFilesystems = { - ext4 = true; - }; - systemd.services.decrypt-root = { - description = "Decrypt ZFS root pool"; - wantedBy = [ "initrd.target" ]; - after = [ "zfs-import.target" ]; - before = [ - "create-needed-for-boot-dirs.service" - "defenestrate.service" - "sysroot.mount" - ]; - onFailure = [ "emergency.target" ]; - unitConfig.DefaultDependencies = "no"; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = "yes"; - }; - script = '' - systemd-cryptsetup attach key /dev/zvol/${config.networking.hostName}/key || exit 1 - mount /dev/mapper/key /key --mkdir || exit 1 - zfs load-key -a || exit 1 - umount /key || exit 1 - systemd-cryptsetup detach key || exit 1 - ''; - }; - }; - zfs = { - forceImportRoot = false; - allowHibernation = true; - requestEncryptionCredentials = false; - }; - }; + services.zfs.autoScrub.enable = true; + + # katja: for some unknown reason userborn doesn't work when zfs is usded - needs to be investigated later + services.userborn.enable = lib.mkForce false; - disko.devices.zpool = { - ${config.networking.hostName} = { - type = "zpool"; - options.ashift = "12"; - rootFsOptions = lib.mkMerge [ - { - acltype = "posixacl"; - canmount = "off"; - compression = "zstd-6"; - mountpoint = "none"; - xattr = "sa"; - } - (lib.mkIf config.modules.filesystem.encrypt { - encryption = "on"; - keyformat = "hex"; - keylocation = "file:///key/${config.networking.hostName}.key"; - }) - ]; - datasets = lib.mkMerge ( - lib.flatten [ - { - data = { - type = "zfs_fs"; - options.canmount = "off"; + boot = lib.mkMerge [ + ( + lib.mkIf cfg.encrypt { + initrd = { + luks.forceLuksSupportInInitrd = true; + supportedFilesystems = { + ext4 = true; }; - "data/home" = { - type = "zfs_fs"; - options = { - canmount = "off"; - mountpoint = "none"; + systemd.services.decrypt-root = { + description = "Decrypt ZFS root pool"; + wantedBy = [ "initrd.target" ]; + after = [ "zfs-import.target" ]; + before = [ + "create-needed-for-boot-dirs.service" + "defenestrate.service" + "sysroot.mount" + ]; + onFailure = [ "emergency.target" ]; + unitConfig.DefaultDependencies = "no"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; }; + script = '' + systemd-cryptsetup attach key /dev/zvol/${hostConfig.hostName}/key || exit 1 + mount /dev/mapper/key /key --mkdir || exit 1 + zfs load-key -a || exit 1 + umount /key || exit 1 + systemd-cryptsetup detach key || exit 1 + ''; }; - "data/system" = { - type = "zfs_fs"; - options.mountpoint = "legacy"; - mountpoint = "/nix/persist/system"; - }; - nix = { - type = "zfs_fs"; - options = { - atime = "off"; - mountpoint = "legacy"; + }; + zfs.requestEncryptionCredentials = false; + } + ) + ( + lib.mkIf hostConfig.hardware.allowHibernation { + zfs = { + forceImportRoot = false; + allowHibernation = true; + }; + } + ) + ]; + + fileSystems."/nix/persist" = lib.mkIf cfg.parts.system { + neededForBoot = true; + }; + + disko.devices = { + zpool = { + "${hostConfig.hostName}" = { + type = "zpool"; + options.ashift = "12"; + rootFsOptions = lib.mkMerge [ + { + acltype = "posixacl"; + devices = "off"; + canmount = "off"; + compression = "zstd-6"; + exec = "off"; + mountpoint = "none"; + setuid = "off"; + xattr = "sa"; + } + ( + lib.mkIf cfg.encrypt { + encryption = "on"; + keyformat = "hex"; + keylocation = "file:///key/${hostConfig.hostName}.key"; + } + ) + ]; + datasets = lib.mkMerge [ + { + "reserved" = { + type = "zfs_volume"; + size = cfg.reservedSpace; + options.readonly = "on"; }; - mountpoint = "/nix"; - }; - os = { - type = "zfs_fs"; - options.canmount = "off"; - }; - "os/nixos" = { - type = "zfs_fs"; - options.canmount = "off"; - }; - "os/nixos/root-1" = { - type = "zfs_fs"; - options = { - atime = "off"; - compression = "zstd-fast"; - mountpoint = "legacy"; + "os" = { + type = "zfs_fs"; + options.canmount = "off"; }; - mountpoint = "/"; - }; - reserved = { - type = "zfs_volume"; - size = "8G"; - options.readonly = "on"; - }; - } - (lib.mkIf config.modules.filesystem.encrypt { - key = { - type = "zfs_volume"; - size = "24M"; - options.encryption = "off"; - # TODO: luksFormat, add key and make readonly - }; - }) - (lib.map (user: { - "data/home/${user}" = { - type = "zfs_fs"; - options.mountpoint = "legacy"; - mountOptions = [ "nofail" ]; - mountpoint = - # if config.modules.filesystem.impermanence.persistHome then - if true then "/home/${user}" else "/nix/persist/home/${user}"; - }; - }) (lib.attrNames (lib.filterAttrs (name: value: value.enable == true) config.modules.users))) - ] - ); - }; - }; - }) - { - disko.devices.disk = { - ${config.networking.hostName} = { - type = "disk"; - device = "${cfg.path}"; - content = { - type = "gpt"; - partitions = lib.mkMerge [ - (lib.mkIf (config.modules.boot.type == "legacy") { - grub-mbr = { - size = "1M"; - type = "EF02"; - priority = 1; - }; - }) - { - boot = { - type = lib.mkIf (config.modules.boot.type == "uefi") "EF00"; - size = "1G"; - content = { - type = "filesystem"; - format = "vfat"; - mountOptions = [ - "nofail" - "umask=0077" - "dmask=0077" - ]; - mountpoint = "/boot"; + "os/nixos" = { + type = "zfs_fs"; + options.canmount = "off"; }; - }; - } - { - root.content = part "root" ( - lib.mkMerge [ - (lib.mkIf (cfg.type == "ext4") { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; - }) - (lib.mkIf (cfg.type == "btrfs") { - type = "btrfs"; - subvolumes = { - "/nixos/@" = { - mountpoint = "/"; - }; - "/nixos/@home" = { - mountOptions = [ "compress=zstd" ]; - mountpoint = "/home"; - }; - "/nixos/@nix" = { + "os/nixos/root-1" = { + type = "zfs_fs"; + options.mountpoint = "legacy"; + mountpoint = "/"; + }; + } + ( + lib.mkIf cfg.parts.nix { + nix = { + type = "zfs_fs"; + options = { + atime = "off"; + devices = "off"; + exec = "on"; + mountpoint = "legacy"; + }; + mountpoint = "/nix"; + }; + } + ) + ( + lib.mkIf (cfg.parts.system || cfg.parts.home) { + data = { + type = "zfs_fs"; + options.canmount = "off"; + }; + } + ) + ( + lib.mkIf (cfg.parts.home && !cfg.parts.homePerUser) { + "data/home" = { + type = "zfs_fs"; + options.mountpoint = "legacy"; + mountpoint = "/home"; + mountOptions = [ + "nofail" + ]; + }; + } + ) + ( + lib.mkIf (cfg.parts.home && cfg.parts.homePerUser) { + "data/home" = { + type = "zfs_fs"; + options.canmount = "off"; + }; + } + ) + ( + lib.mkIf cfg.parts.homePerUser ( + lib.listToAttrs ( + lib.map (user: { + name = "data/home/${user}"; + value = { + type = "zfs_fs"; + options = { + devices = "off"; + mountpoint = "legacy"; + setuid = "off"; + }; mountOptions = [ - "compress=zstd" - "noatime" + "nofail" ]; - mountpoint = "/nix"; + mountpoint = if config.modules.filesystem.impermanence.home.enable then "/nix/persist/home/${user}" else "/home/${user}"; }; + }) users + ) + ) + ) + ( + lib.mkIf cfg.parts.system { + "data/persist" = { + type = "zfs_fs"; + options = { + devices = "off"; + mountpoint = "legacy"; + setuid = "off"; }; - }) - (lib.mkIf (cfg.type == "zfs") { - type = "zfs"; - pool = config.networking.hostName; - }) - ] - ); - } - ( - if cfg.swap.enable then - { + mountpoint = "/nix/persist"; + }; + } + ) + ( + lib.mkIf cfg.encrypt { + key = { + type = "zfs_volume"; + size = "50M"; + options.encryption = "off"; + # TODO: luksFormat, add key and make readonly + # cryptsetup luksFormat /dev/zvol/<host>/key + # cryptsetup open /dev/zvol/<host>/key key + # mkfs.ext4 -O ^has_journal /dev/mapper/key + # mount /dev/mapper/key /key + }; + } + ) + ]; + }; + }; + }; + } + ) + { + disko.devices = { + nodev = lib.mkIf cfg.parts.tmp { + "/tmp" = { + fsType = "tmpfs"; + mountOptions = [ + "nosuid" + "nodev" + ]; + }; + }; + disk = { + # TODO: Add multidisk support + "${hostConfig.hostName}" = { + type = "disk"; + device = "${cfg.path}"; + content = { + type = "gpt"; + partitions = lib.mkMerge [ + (lib.mkIf (config.modules.boot.type == "legacy") { + grub-mbr = { + size = "1M"; + type = "EF02"; + priority = 1; + }; + }) + { + boot = { + size = "700M"; + content = { + type = "filesystem"; + format = "vfat"; + mountOptions = [ + "nofail" + "umask=0077" + "dmask=0077" + ]; + mountpoint = "/boot"; + }; + }; + } + ( + lib.mkIf (config.modules.boot.type == "uefi") { + boot.type = "EF00"; + } + ) + + { + root.content = ( + lib.mkMerge [ + ( + lib.mkIf (cfg.type == "ext4") ( + part "root" { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + } + ) + ) + ( + lib.mkIf (cfg.type == "btrfs") ( + part "root" { + type = "btrfs"; + subvolumes = { + "/nixos/@" = { + mountpoint = "/"; + }; + "/nixos/@home" = { + mountOptions = [ "compress=zstd" ]; + mountpoint = "/home"; + }; + "/nixos/@nix" = { + mountOptions = [ + "compress=zstd" + "noatime" + ]; + mountpoint = "/nix"; + }; + }; + } + ) + ) + ( + lib.mkIf (cfg.type == "zfs") { + type = "zfs"; + pool = hostConfig.hostName; + } + ) + ] + ); + } + ( + if cfg.swap.enable then { root.end = "-${cfg.swap.size}"; swap = { size = "100%"; content = part "swap" { type = "swap"; }; }; + } else { + root.size = "100%"; } - else - { root.size = "100%"; } - ) - ]; + ) + ]; + }; }; }; };