diff --git a/disk-configs/zfs-nas.nix b/disk-configs/zfs-nas.nix new file mode 100644 index 00000000..2d07016a --- /dev/null +++ b/disk-configs/zfs-nas.nix @@ -0,0 +1,124 @@ +# ZFS NAS disk configuration +# Boot disk (ext4) + ZFS data pool for document/media storage +# +# Deployment notes: +# Disk assignments use lib.mkDefault so nixos-anywhere (or a host-specific +# overlay) can override the actual device paths at install time. +# +# For mirror or RAIDZ layouts, add additional disks below that reference +# pool = "tank" and set `disko.devices.zpool.tank.mode` accordingly +# (e.g. "mirror", "raidz1", "raidz2"). +{lib, ...}: { + disko.devices = { + disk = { + # Boot disk - small SSD for OS + boot = { + device = lib.mkDefault "/dev/sda"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + root = { + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + }; + }; + }; + }; + + # First data disk - member of the "tank" zpool. + # For mirrors or RAIDZ, copy this block as `tank1`, `tank2`, etc., + # pointing each at a distinct device. + tank0 = { + device = lib.mkDefault "/dev/sdb"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "tank"; + }; + }; + }; + }; + }; + }; + + zpool = { + # Main data pool. Disks are assigned via the partitions above + # (content.type = "zfs"; content.pool = "tank";). + # Change `mode` to "mirror", "raidz1", "raidz2" when adding more disks. + tank = { + type = "zpool"; + + rootFsOptions = { + compression = "zstd"; + "com.sun:auto-snapshot" = "false"; + }; + + options.ashift = "12"; + + datasets = { + # Container for all data + data = { + type = "zfs_fs"; + mountpoint = "/tank/data"; + options = { + compression = "zstd"; + atime = "off"; + }; + }; + + # Paperless document storage + paperless = { + type = "zfs_fs"; + mountpoint = "/var/lib/paperless"; + options = { + compression = "zstd"; + recordsize = "1M"; # Good for documents + atime = "off"; + }; + }; + + # Backups and snapshots + backup = { + type = "zfs_fs"; + mountpoint = "/tank/backup"; + options = { + compression = "zstd"; + atime = "off"; + }; + }; + + # General media storage + media = { + type = "zfs_fs"; + mountpoint = "/tank/media"; + options = { + compression = "zstd"; + recordsize = "1M"; + atime = "off"; + }; + }; + }; + }; + }; + }; +} diff --git a/flake.nix b/flake.nix index 0a10e7e3..ec9b6871 100644 --- a/flake.nix +++ b/flake.nix @@ -449,6 +449,56 @@ ]; }; + # NAS - Network Attached Storage with ZFS and paperless-ngx + "type-nas" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = inputs // {inherit inputs;}; + modules = [ + configuration + ./modules + ./modules/nixos/base.nix + home-manager.nixosModules.home-manager + {home-manager.sharedModules = [opnix.homeManagerModules.default];} + + # Disk layout - ZFS for data redundancy + inputs.disko.nixosModules.disko + ./disk-configs/zfs-nas.nix + + # Machine type configuration (includes myConfig, hardware.facter, SSH keys) + ./machine-types/server.nix + + # NAS-specific services (paperless, ZFS support) + ./targets/type-nas + ]; + }; + + # CATTLE CONFIGURATIONS - Generic machine types + # These require no hardware-configuration.nix! + # Use with: ./scripts/install-machine.sh + + "type-desktop" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = inputs // {inherit inputs;}; + modules = [ + configuration + ./modules + ./modules/nixos/base.nix + home-manager.nixosModules.home-manager + {home-manager.sharedModules = [opnix.homeManagerModules.default];} + + # Disk layout + inputs.disko.nixosModules.disko + ./disk-configs/zero.nix + + # Machine type configuration (includes myConfig defaults and SSH keys) + ./machine-types/desktop.nix + + # Ghostty terminfo for SSH support + # https://github.com/ghostty-org/ghostty/discussions/5753 + ./modules/nixos/ghostty-terminfo.nix + ]; + }; + # Foundation-based server configuration # Minimal required fields: system architecture, SSH authorized keys "type-server" = nixpkgs.lib.nixosSystem { diff --git a/targets/type-nas/default.nix b/targets/type-nas/default.nix new file mode 100644 index 00000000..35347710 --- /dev/null +++ b/targets/type-nas/default.nix @@ -0,0 +1,77 @@ +# type-nas - Network Attached Storage with document management +# Cattle configuration: ZFS storage + paperless-ngx for document archival +{ + pkgs, + lib, + ... +}: { + myConfig.autoUpgrade.flakeUrl = lib.mkForce "github:funkymonkeymonk/nix#type-nas"; + + # ZFS support + boot.supportedFilesystems = ["zfs"]; + boot.zfs.forceImportRoot = false; + + # ZFS kernel module + boot.kernelModules = ["zfs"]; + + # ZFS requires a unique 8-hex-digit host ID. Use a stable default here so + # the config evaluates deterministically; override per-host if needed. + networking.hostId = lib.mkDefault "8badf00d"; + + # Paperless-ngx document management + services.paperless = { + enable = true; + address = "0.0.0.0"; + port = 28981; + dataDir = "/var/lib/paperless"; + + # Configure with sensible defaults + settings = { + PAPERLESS_OCR_LANGUAGE = "eng"; + PAPERLESS_CONSUMER_RECURSIVE = "true"; + PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = "true"; + }; + }; + + # Ensure paperless can consume from a watched directory + # Note: ZFS dataset handles main storage, this creates the consume subdirectory + systemd.tmpfiles.rules = [ + "d /var/lib/paperless/consume 0755 paperless paperless -" + ]; + + # Open firewall for paperless web UI + networking.firewall.allowedTCPPorts = [28981]; + + # ZFS maintenance services + services.zfs.autoScrub.enable = true; + services.sanoid = { + enable = true; + templates = { + # Daily snapshots, keep for a month + daily = { + hourly = 24; + daily = 30; + monthly = 3; + autosnap = true; + autoprune = true; + }; + }; + datasets = { + "tank/paperless" = { + useTemplate = ["daily"]; + }; + "tank/data" = { + useTemplate = ["daily"]; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + vim + htop + curl + zfs + sanoid + parted + ]; +}