{ config, lib, ... }: # The host-level ZFS pool import: durable service state a host mounts, never rebuilds. let cfg = config.modules.host.zfs; # NOLINT(let_order) in { options.modules.host.zfs = { enable = lib.mkEnableOption "importing durable ZFS pools that hold service state"; pools = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.path); default = { }; example = lib.literalExpression '' { tank = { media = "/srv/media"; downloads = "/srv/downloads"; }; } ''; description = '' The ZFS pools to import at boot, keyed by pool name, each pool mapping a dataset path relative to it to that dataset's mountpoint. A pool is durable state imported as it stands, never created or destroyed by a rebuild, so a service's data survives any rebuild or reimage. A pool with an empty map is still imported, leaving each dataset to its own ZFS `mountpoint` property. ''; }; }; config = lib.mkIf cfg.enable { # The ZFS stack in the kernel and boot, needed even where the root filesystem is another kind. boot.supportedFilesystems = [ "zfs" ]; # Derive a stable 32-bit host ID from the configured hostname. networking.hostId = builtins.substring 0 8 ( builtins.hashString "sha256" config.networking.hostName ); # The declared pools are imported at boot, distinct from any pool backing the root filesystem. boot.zfs.extraPools = lib.attrNames cfg.pools; # Each declared dataset is mounted at its host path as a native ZFS filesystem. fileSystems = lib.mkMerge ( lib.mapAttrsToList ( pool: mounts: lib.mapAttrs' ( dataset: mountpoint: lib.nameValuePair mountpoint { device = "${pool}/${dataset}"; fsType = "zfs"; } ) mounts ) cfg.pools ); }; }