diff --git a/machines/nix-book/configuration.nix b/machines/nix-book/configuration.nix index 9b66099..f6c0fe9 100644 --- a/machines/nix-book/configuration.nix +++ b/machines/nix-book/configuration.nix @@ -39,14 +39,6 @@ boot.kernelPackages = pkgs.linuxPackages_latest; - # Btrfs deduplication service - services.beesd.filesystems = { - root = { - spec = "/"; - hashTableSizeMB = 32; # 128MB per TB recommended, ~225GB = ~32MB - verbosity = "err"; # Only show actual problems - }; - }; # Enable networking networking.networkmanager.enable = true; diff --git a/machines/nix-book/hardware-configuration.nix b/machines/nix-book/hardware-configuration.nix index 6af39bc..42e8b3f 100644 --- a/machines/nix-book/hardware-configuration.nix +++ b/machines/nix-book/hardware-configuration.nix @@ -10,19 +10,27 @@ boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "vmd" "nvme" "sdhci_pci" ]; boot.initrd.kernelModules = [ ]; + boot.initrd.luks.devices."luks-4126fbd4-bd09-4ece-af0d-6fff414c21b3".device = "/dev/disk/by-uuid/4126fbd4-bd09-4ece-af0d-6fff414c21b3"; boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; - fileSystems."/" = - { device = "/dev/disk/by-uuid/223a44e5-91e2-4272-830e-129166042a1d"; - fsType = "btrfs"; - options = [ - "compress=zstd" # Enable zstd compression for space savings - "noatime" # Don't update access times for performance - ]; + roles.btrfs = { + enable = true; + filesystems."/dev/disk/by-uuid/223a44e5-91e2-4272-830e-129166042a1d" = { + mountpoints = { + "/" = { + compression = "zstd"; + extraOptions = [ "noatime" ]; + }; + }; + scrub.enable = true; + deduplication = { + enable = true; + hashTableSizeMB = 32; + verbosity = "err"; + }; }; - - boot.initrd.luks.devices."luks-4126fbd4-bd09-4ece-af0d-6fff414c21b3".device = "/dev/disk/by-uuid/4126fbd4-bd09-4ece-af0d-6fff414c21b3"; + }; fileSystems."/boot" = { device = "/dev/disk/by-uuid/7A0B-CF88"; diff --git a/roles/btrfs/default.nix b/roles/btrfs/default.nix new file mode 100644 index 0000000..394592b --- /dev/null +++ b/roles/btrfs/default.nix @@ -0,0 +1,168 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.roles.btrfs; +in +{ + options.roles.btrfs = { + enable = mkEnableOption "Enable btrfs filesystem management"; + + filesystems = mkOption { + type = types.attrsOf (types.submodule { + options = { + # Filesystem-level maintenance options + scrub = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable automatic scrubbing for this filesystem"; + }; + interval = mkOption { + type = types.str; + default = "weekly"; + description = "Scrub interval (systemd timer format)"; + }; + }; + deduplication = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable beesd deduplication for this filesystem"; + }; + hashTableSizeMB = mkOption { + type = types.int; + default = 1024; + description = "Hash table size in MB (should be multiple of 16)"; + }; + verbosity = mkOption { + type = types.str; + default = "info"; + description = "Logging verbosity level"; + }; + }; + balance = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable periodic balance operations"; + }; + interval = mkOption { + type = types.str; + default = "monthly"; + description = "Balance interval (systemd timer format)"; + }; + dataUsage = mkOption { + type = types.int; + default = 50; + description = "Data usage threshold for balance"; + }; + metadataUsage = mkOption { + type = types.int; + default = 50; + description = "Metadata usage threshold for balance"; + }; + }; + + # Mountpoint-based configuration + mountpoints = mkOption { + type = types.attrsOf (types.submodule { + options = { + subvolume = mkOption { + type = types.nullOr types.str; + default = null; + description = "Subvolume name. If null, uses default subvolume."; + }; + compression = mkOption { + type = types.str; + default = "zstd"; + description = "Compression algorithm (zstd, lzo, lz4, none)"; + }; + autodefrag = mkOption { + type = types.bool; + default = false; + description = "Enable automatic defragmentation"; + }; + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + description = "Additional mount options"; + }; + }; + }); + default = {}; + description = "Mountpoint configurations for this filesystem"; + }; + }; + }); + default = {}; + description = "Btrfs filesystems configuration"; + }; + }; + + config = mkIf cfg.enable { + # Generate fileSystems configuration from mountpoints + fileSystems = mkMerge (flatten (mapAttrsToList (device: fsCfg: + mapAttrsToList (mountpoint: mountCfg: + { + ${mountpoint} = { + device = device; + fsType = "btrfs"; + options = + (optional (mountCfg.subvolume != null) "subvol=${mountCfg.subvolume}") ++ + [ "compress=${mountCfg.compression}" ] ++ + (optional mountCfg.autodefrag "autodefrag") ++ + mountCfg.extraOptions; + }; + } + ) fsCfg.mountpoints + ) cfg.filesystems)); + + # Configure scrub service using NixOS built-in + services.btrfs.autoScrub = mkIf (any (fs: fs.scrub.enable) (attrValues cfg.filesystems)) { + enable = true; + interval = "weekly"; # TODO: Make this configurable per filesystem + fileSystems = attrNames (filterAttrs (_: fs: fs.scrub.enable) cfg.filesystems); + }; + + # Configure beesd for filesystems with deduplication enabled + services.beesd.filesystems = mapAttrs' (device: fsCfg: + nameValuePair (replaceStrings ["/"] ["_"] (replaceStrings ["-"] ["_"] device)) { + spec = device; + hashTableSizeMB = fsCfg.deduplication.hashTableSizeMB; + verbosity = fsCfg.deduplication.verbosity; + } + ) (filterAttrs (_: fs: fs.deduplication.enable) cfg.filesystems); + + # Custom balance services for filesystems with balance enabled + systemd.services = mkMerge (mapAttrsToList (device: fsCfg: mkIf fsCfg.balance.enable { + "btrfs-balance-${replaceStrings ["/"] ["-"] (replaceStrings ["-"] ["_"] device)}" = { + description = "Balance btrfs filesystem ${device}"; + script = '' + ${pkgs.btrfs-progs}/bin/btrfs balance start \ + -dusage=${toString fsCfg.balance.dataUsage} \ + -musage=${toString fsCfg.balance.metadataUsage} \ + ${device} + ''; + serviceConfig = { + Type = "oneshot"; + Nice = 19; + IOSchedulingClass = "idle"; + }; + }; + }) cfg.filesystems); + + # Balance timers + systemd.timers = mkMerge (mapAttrsToList (device: fsCfg: mkIf fsCfg.balance.enable { + "btrfs-balance-${replaceStrings ["/"] ["-"] (replaceStrings ["-"] ["_"] device)}" = { + description = "Periodic balance for ${device}"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = fsCfg.balance.interval; + Persistent = true; + }; + }; + }) cfg.filesystems); + }; +} \ No newline at end of file diff --git a/roles/default.nix b/roles/default.nix index 1f876ed..d57fcfe 100644 --- a/roles/default.nix +++ b/roles/default.nix @@ -6,6 +6,7 @@ with lib; imports = [ ./audio ./bluetooth + ./btrfs ./desktop ./kodi ./nfs-mounts