feat(core): initialize NaxOS declarative NixOS distribution, modules, and ISO installer
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.naxos.storage;
|
||||
in {
|
||||
options.services.naxos.storage = {
|
||||
enable = mkEnableOption "NaxOS Declarative ZFS Storage Engine";
|
||||
|
||||
arcMaxBytes = mkOption {
|
||||
type = types.nullOr types.ints.positive;
|
||||
default = 4294967296; # 4 GiB default
|
||||
description = "Maximum ZFS ARC cache size in bytes.";
|
||||
};
|
||||
|
||||
autoScrub = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable automated regular scrubbing of ZFS pools.";
|
||||
};
|
||||
interval = mkOption {
|
||||
type = types.str;
|
||||
default = "monthly";
|
||||
description = "Interval for auto-scrub (e.g. monthly, weekly).";
|
||||
};
|
||||
};
|
||||
|
||||
autoTrim = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable periodic TRIM for SSD/NVMe vdevs.";
|
||||
};
|
||||
};
|
||||
|
||||
autoSnapshot = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable automatic snapshot retention policies.";
|
||||
};
|
||||
hourly = mkOption {
|
||||
type = types.int;
|
||||
default = 24;
|
||||
description = "Number of hourly snapshots to keep.";
|
||||
};
|
||||
daily = mkOption {
|
||||
type = types.int;
|
||||
default = 7;
|
||||
description = "Number of daily snapshots to keep.";
|
||||
};
|
||||
weekly = mkOption {
|
||||
type = types.int;
|
||||
default = 4;
|
||||
description = "Number of weekly snapshots to keep.";
|
||||
};
|
||||
monthly = mkOption {
|
||||
type = types.int;
|
||||
default = 12;
|
||||
description = "Number of monthly snapshots to keep.";
|
||||
};
|
||||
};
|
||||
|
||||
importExistingPools = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "tank" "datapool" ];
|
||||
description = "List of existing foreign ZFS pools (e.g. from TrueNAS or nixos-lukas) to safely import without formatting.";
|
||||
};
|
||||
|
||||
pools = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether to manage this pool.";
|
||||
};
|
||||
ashift = mkOption {
|
||||
type = types.int;
|
||||
default = 12;
|
||||
description = "ZFS vdev ashift alignment value (12 = 4K sectors).";
|
||||
};
|
||||
layout = mkOption {
|
||||
type = types.enum [ "stripe" "mirror" "raidz1" "raidz2" "raidz3" ];
|
||||
default = "mirror";
|
||||
description = "Vdev topology layout.";
|
||||
};
|
||||
devices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "/dev/disk/by-id/nvme-..." "/dev/disk/by-id/ata-..." ];
|
||||
description = "Member disk devices or partitions.";
|
||||
};
|
||||
datasets = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
mountpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Mountpoint for this dataset. If null, uses default ZFS mount.";
|
||||
};
|
||||
compression = mkOption {
|
||||
type = types.enum [ "on" "off" "lz4" "zstd" "zstd-fast" "gzip" ];
|
||||
default = "lz4";
|
||||
description = "ZFS compression algorithm.";
|
||||
};
|
||||
recordsize = mkOption {
|
||||
type = types.str;
|
||||
default = "128K";
|
||||
description = "Record size for dataset (e.g. 1M for media, 16K for databases).";
|
||||
};
|
||||
quota = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Optional dataset quota (e.g. 500G).";
|
||||
};
|
||||
reservation = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Optional dataset reservation.";
|
||||
};
|
||||
owner = mkOption {
|
||||
type = types.str;
|
||||
default = "root";
|
||||
description = "POSIX owner of mountpoint.";
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "root";
|
||||
description = "POSIX group of mountpoint.";
|
||||
};
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "0755";
|
||||
description = "POSIX directory mode permissions.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Sub-datasets belonging to this pool.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Declaratively defined ZFS pools and datasets.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
boot.kernelPackages = pkgs.linuxPackages;
|
||||
|
||||
# Kernel parameter for ZFS ARC Max limit
|
||||
boot.kernelParams = mkIf (cfg.arcMaxBytes != null) [
|
||||
"zfs.zfs_arc_max=${toString cfg.arcMaxBytes}"
|
||||
];
|
||||
|
||||
# Safe pool import list (includes both configured pools and imported existing pools)
|
||||
boot.zfs.extraPools = unique (
|
||||
(attrNames (filterAttrs (n: v: v.enable) cfg.pools)) ++
|
||||
cfg.importExistingPools
|
||||
);
|
||||
boot.zfs.forceImportRoot = false;
|
||||
|
||||
# Automated ZFS services
|
||||
services.zfs = {
|
||||
autoScrub = {
|
||||
enable = cfg.autoScrub.enable;
|
||||
interval = cfg.autoScrub.interval;
|
||||
};
|
||||
trim = {
|
||||
enable = cfg.autoTrim.enable;
|
||||
};
|
||||
autoSnapshot = {
|
||||
enable = cfg.autoSnapshot.enable;
|
||||
hourly = cfg.autoSnapshot.hourly;
|
||||
daily = cfg.autoSnapshot.daily;
|
||||
weekly = cfg.autoSnapshot.weekly;
|
||||
monthly = cfg.autoSnapshot.monthly;
|
||||
};
|
||||
};
|
||||
|
||||
# System tools for storage diagnosis and disk monitoring
|
||||
environment.systemPackages = with pkgs; [
|
||||
zfs
|
||||
smartmontools
|
||||
hdparm
|
||||
nvme-cli
|
||||
parted
|
||||
gptfdisk
|
||||
iotop
|
||||
ncdu
|
||||
sanoid
|
||||
syncoid
|
||||
];
|
||||
|
||||
# Generate systemd.tmpfiles rules for configured datasets with custom owners/modes
|
||||
systemd.tmpfiles.rules = flatten (mapAttrsToList (poolName: poolCfg:
|
||||
mapAttrsToList (dsName: dsCfg:
|
||||
mkIf (dsCfg.mountpoint != null)
|
||||
"d ${dsCfg.mountpoint} ${dsCfg.mode} ${dsCfg.owner} ${dsCfg.group} -"
|
||||
) poolCfg.datasets
|
||||
) cfg.pools);
|
||||
|
||||
# Generate fileSystems definitions for datasets with explicit mountpoints
|
||||
fileSystems = foldl' (acc: pool:
|
||||
let
|
||||
poolName = pool.name;
|
||||
poolCfg = pool.value;
|
||||
in acc // (mapAttrs' (dsName: dsCfg:
|
||||
nameValuePair dsCfg.mountpoint {
|
||||
device = "${poolName}/${dsName}";
|
||||
fsType = "zfs";
|
||||
options = [ "nofail" ];
|
||||
}
|
||||
) (filterAttrs (n: v: v.mountpoint != null) poolCfg.datasets))
|
||||
) {} (mapAttrsToList (name: value: { inherit name value; }) (filterAttrs (n: v: v.enable) cfg.pools));
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user