{ config, lib, pkgs, ... }: with lib; let cfg = config.numbus.hardware; mkDataDisk = idx: device: { name = "content-${toString idx}"; value = { type = "disk"; device = device; content = { type = "gpt"; partitions = { luks = { size = "100%"; content = { type = "luks"; name = "crypted-content-${toString idx}"; initrdUnlock = false; settings = { keyFile = "/etc/secrets/disks/content-${toString idx}"; allowDiscards = true; crypttabExtraOpts = [ "nofail" ]; }; content = { type = "filesystem"; format = cfg.dataDisksFilesystem; mountpoint = "/mnt/content-${toString idx}"; mountOptions = [ "nofail" "noauto" ]; }; }; }; }; }; }; }; mkParityDisk = idx: device: { name = "parity-${toString idx}"; value = { type = "disk"; device = device; content = { type = "gpt"; partitions = { luks = { size = "100%"; content = { type = "luks"; name = "crypted-parity-${toString idx}"; initrdUnlock = false; settings = { keyFile = "/etc/secrets/disks/parity-${toString idx}"; allowDiscards = true; crypttabExtraOpts = [ "nofail" ]; }; content = { type = "filesystem"; format = cfg.parityDisksFilesystem; mountpoint = "/mnt/parity-${toString idx}"; mountOptions = [ "nofail" "noauto" ]; }; }; }; }; }; }; }; isMirror = length cfg.bootDisksList > 1; bootDisksConfig = if isMirror then { mdadm = { boot = { type = "mdadm"; level = 1; metadata = "1.2"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; }; disk = listToAttrs (imap1 (i: device: { name = "boot-${toString i}"; value = { type = "disk"; device = device; content = { type = "gpt"; partitions = { ESP = { size = "1G"; type = "EF00"; content = { type = "mdraid"; name = "boot"; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted-boot-${toString i}"; settings = { allowDiscards = true; keyFile = "/etc/secrets/disks/boot-${toString i}"; }; content = { type = "lvm_pv"; vg = "pool"; }; }; }; }; }; }; }) cfg.bootDisksList); } else { disk = { "boot-1" = { type = "disk"; device = head cfg.bootDisksList; content = { type = "gpt"; partitions = { ESP = { size = "1G"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; luks = { size = "100%"; content = { type = "luks"; name = "crypted-boot-1"; settings = { keyFile = "/etc/secrets/disks/boot-1"; allowDiscards = true; }; content = { type = "lvm_pv"; vg = "pool"; }; }; }; }; }; }; }; }; lvmConfig = { lvm_vg = { pool = { type = "lvm_vg"; lvs = { swap = { size = cfg.swapSize; content = { type = "swap"; }; } // optionalAttrs isMirror { lvm_type = "mirror"; }; snapraid = { size = "1G"; content = { type = "filesystem"; format = "btrfs"; mountpoint = "/mnt/content-0"; }; } // optionalAttrs isMirror { lvm_type = "mirror"; }; root = { size = "100%"; content = { type = "btrfs"; extraArgs = [ "-f" ]; subvolumes = { "/rootfs" = { mountpoint = "/"; mountOptions = [ "compress=zstd" "noatime" ]; }; "/home" = { mountpoint = "/home"; mountOptions = [ "compress=zstd" ]; }; "/nix" = { mountpoint = "/nix"; mountOptions = [ "compress=zstd" "noatime" ]; }; }; }; } // optionalAttrs isMirror { lvm_type = "mirror"; }; }; }; }; }; in { options.numbus.hardware = { dataDisksList = mkOption { type = types.listOf types.str; default = []; example = [ "/dev/disk/by-id/WD_Blue_ATO431_159Ejz224G0000382b" "/dev/disk/by-id/Seagate_Barracuda_159Ejz224G" ]; description = "List by-id path of devices for data disks"; }; parityDisksList = mkOption { type = types.listOf types.str; default = []; example = [ "/dev/disk/by-id/WD_Blue_ATO431_159Ejz224G0000382b" "/dev/disk/by-id/Seagate_Barracuda_159Ejz224G" ]; description = "List of by-id path of devices for parity disks"; }; bootDisksList = mkOption { type = types.listOf types.str; default = []; example = [ "/dev/disk/by-id/nvme_SAMSUNG_MZVPYEHCO_159Ejz224G0000" "/dev/disk/by-id/ata-San_Disk_159Ejz224G" ]; description = "List of by-id path of devices for boot disks"; }; spindownDisksList = mkOption { type = types.listOf types.str; default = []; example = [ "/dev/disk/by-id/WD_Blue_ATO431_159Ejz224G0000382b" "/dev/disk/by-id/Seagate_Barracuda_159Ejz224G" ]; description = "List of by-id path of devices to spindown when inactive to save power (HDD only)"; }; swapSize = mkOption { type = types.str; default = "16G"; example = "16G"; description = "Size of the swap partition"; }; dataDisksFilesystem = mkOption { type = types.enum [ "xfs" "ext4" "btrfs" ]; default = "xfs"; example = "xfs"; description = "Filesystem for data disks. Available filesystem options : xfs, ext4, btrfs"; }; parityDisksFilesystem = mkOption { type = types.enum [ "xfs" "ext4" "btrfs" ]; default = "xfs"; example = "xfs"; description = "Filesystem for parity disks. Available filesystem options : xfs, ext4, btrfs"; }; }; config = mkIf (cfg.bootDisksList != []) { disko.devices = mkMerge [ bootDisksConfig lvmConfig { disk = listToAttrs (imap1 mkDataDisk cfg.dataDisksList); } { disk = listToAttrs (imap1 mkParityDisk cfg.parityDisksList); } ]; services.snapraid = { enable = true; contentFiles = [ "/mnt/content-0/snapraid.content" ] ++ (map (i: "/mnt/content-${toString i}/snapraid.content") (range 1 (length cfg.dataDisksList))); parityFiles = map (i: "/mnt/parity-${toString i}/snapraid.parity") (range 1 (length cfg.parityDisksList)); dataDisks = listToAttrs (imap1 (i: _: nameValuePair "d${toString i}" "/mnt/content-${toString i}") cfg.dataDisksList); }; fileSystems."/mnt/data" = { device = "/mnt/content-*"; fsType = "fuse.mergerfs"; options = [ "category.create=ff" "cache.files=partial" "dropcacheonclose=true" "defaults" "noauto" "nofail" "allow_other" "moveonenospc=1" "minfreespace=50G" "func.getattr=newest" "fsname=mergerfs_data" "x-mount.mkdir" "x-systemd.automount" ]; }; systemd.services.mount-disks = { description = "Mount data and parity disks"; before = [ "mnt-data.mount" ]; requiredBy = [ "mnt-data.mount" ]; path = [ pkgs.cryptsetup pkgs.util-linux ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = let mountDataDisk = i: '' if [ ! -e /dev/mapper/crypted-content-${toString i} ]; then cryptsetup luksOpen --key-file /etc/secrets/disks/content-${toString i} /dev/disk/by-partlabel/disk-content-${toString i}-luks crypted-content-${toString i} fi mkdir -p /mnt/content-${toString i} if ! mountpoint -q /mnt/content-${toString i}; then mount -t ${cfg.dataDisksFilesystem} /dev/mapper/crypted-content-${toString i} /mnt/content-${toString i} fi ''; mountParityDisk = i: '' if [ ! -e /dev/mapper/crypted-parity-${toString i} ]; then cryptsetup luksOpen --key-file /etc/secrets/disks/parity-${toString i} /dev/disk/by-partlabel/disk-parity-${toString i}-luks crypted-parity-${toString i} fi mkdir -p /mnt/parity-${toString i} if ! mountpoint -q /mnt/parity-${toString i}; then mount -t ${cfg.parityDisksFilesystem} /dev/mapper/crypted-parity-${toString i} /mnt/parity-${toString i} fi ''; in '' ${concatMapStrings mountDataDisk (range 1 (length cfg.dataDisksList))} ${concatMapStrings mountParityDisk (range 1 (length cfg.parityDisksList))} ''; }; }; }