{ config, lib, ... }: let cfg = config.disko; in { options.disko.disks = lib.mkOption { description = lib.mdDoc "Disk names to format."; type = with lib.types; listOf (submodule (_: { options = { device = lib.mkOption { type = lib.types.str; default = null; example = "/dev/sda"; description = "Path of the disk."; }; name = lib.mkOption { type = lib.types.str; default = null; example = "ssd0"; description = "Name of the disk."; }; withBoot = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to include a boot partition."; }; withCeph = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to include a ceph partition."; }; withLuks = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to encrypt the paritions."; }; withZfs = lib.mkOption { type = lib.types.bool; default = true; description = "Wether to include a zfs parition."; }; }; })); default = [ ]; }; config = { assertions = lib.mkIf (cfg.disks != [ ]) (lib.head (map (disk: [ { assertion = disk.withCeph || disk.withZfs; message = "Must enable ceph or zfs!"; } { assertion = disk.withCeph -> disk.withLuks; message = "Ceph requires Luks!"; } ]) cfg.disks)); disko.devices = lib.mkIf (cfg.disks != [ ]) (lib.head (map (disk: let diskName = if disk.name != "" then "-${disk.name}" else ""; luksName = "crypt-${config.networking.hostName}${diskName}"; rootSize = 200; # size of the zfs partition if inside of lvm vgName = "lvm-${config.networking.hostName}${diskName}"; zfs = { size = if (!disk.withCeph) then "100%FREE" else "${toString rootSize}GiB"; content = { pool = zfsName; type = "zfs"; }; }; zfsName = "${config.networking.hostName}${diskName}"; in { disk.${disk.device} = { inherit (disk) device; type = "disk"; content = { type = "table"; format = "gpt"; partitions = lib.optional disk.withZfs { name = "ESP"; start = "1MiB"; end = "512MiB"; bootable = true; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; } ++ [ { name = "root"; start = if disk.withZfs then "512MiB" else "1MiB"; end = "100%"; part-type = "primary"; content = lib.optionalAttrs disk.withLuks { type = "luks"; name = luksName; # TODO: add password, otherwise prompt opens keyFile = "/$PWD/keyFile"; content = { type = "lvm_pv"; vg = vgName; }; } // lib.optionalAttrs (!disk.withLuks) zfs.content; } ]; }; }; } // lib.optionalAttrs disk.withLuks { lvm_vg.${vgName} = { type = "lvm_vg"; lvs = lib.optionalAttrs disk.withCeph { ceph.size = "100%FREE"; } // lib.optionalAttrs disk.withZfs { inherit zfs; }; }; } // { zpool.${zfsName} = { type = "zpool"; mountpoint = "/"; rootFsOptions.acltype = "posixacl"; options = { ashift = "12"; autotrim = "on"; }; datasets = let dataset = mountpoint: { options = { canmount = "on"; compression = "zstd"; dnodesize = "auto"; normalization = "formD"; xattr = "sa"; inherit mountpoint; }; type = "zfs_fs"; }; in { "data" = dataset "/"; "data/etc" = dataset "/etc"; "data/home" = dataset "/home"; "data/var" = dataset "/var"; # used by services.postgresqlBackup and later by restic "data/var/backup" = dataset "/var/backup"; "data/var/lib" = dataset "/var/lib"; "data/var/log" = dataset "/var/log"; "nixos" = { options = { canmount = "off"; mountpoint = "none"; }; type = "zfs_fs"; }; "nixos/nix" = dataset "/nix"; "nixos/nix/store" = { options = { atime = "off"; canmount = "on"; mountpoint = "/nix/store"; }; type = "zfs_fs"; }; "nixos/nix/var" = dataset "/nix/var"; "reserved" = { # zfs uses copy on write and requires some free space to delete files when the disk is completely filled options = { canmount = "off"; mountpoint = "none"; reservation = "5GiB"; }; type = "zfs_fs"; }; }; }; }) cfg.disks)); }; }