From 0ba837817ed0cd83fd1f0909583c1bd29b40e43f Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Mon, 20 Jul 2026 08:27:37 +1000 Subject: [PATCH] Expand variables.nix: NFS shares, ports, image size, GC/rotation, Pi host Adds nested vars.nfsShares (subpath + mountpoint per dataset, previously duplicated independently across server.nix's NFS exports, mount-data.nix's client mounts, docker.nix's tmpfiles rules, traefik's log rotation path, and hosts/server/host.nix's beszel config), vars.ports (every literal port in modules/ and hosts/, kept as separate entries per service even where numbers coincide so changing one can't silently change another), plus vars.proxmoxImageSize, vars.nixCacheGcMaxAge, vars.traefikLogRotate, and raspberryPiHost/raspiNfsPath/raspiMountpoint for the Pi's own NFS export. Also fixes docker.nix/minimal.nix/gui.nix hardcoding the literal "nixos" username instead of the existing vars.primaryUser, found during the sweep. system.stateVersion is deliberately left untouched everywhere -- per NixOS's own docs that value must stay fixed from first install, not follow any shared variable. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot --- README.md | 2 +- hosts/nixos/home.nix | 8 +-- hosts/server/host.nix | 2 +- modules/beszel/enable-agent.nix | 2 +- modules/build-types/docker.nix | 15 ++-- modules/build-types/gui.nix | 4 +- modules/build-types/minimal.nix | 4 +- modules/build-types/pxe-boot.nix | 4 +- modules/build-types/server.nix | 12 ++-- modules/disko/proxmox.nix | 4 +- modules/docker/mount-data.nix | 20 +++--- modules/nix-cache/server.nix | 4 +- modules/raspi/mount-data.nix | 4 +- modules/traefik/rotate-logs.nix | 8 +-- variables.nix | 115 +++++++++++++++++++++++++++++-- 15 files changed, 159 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 016901b..960577f 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]' | Path | Purpose | | --- | --- | | `flake.nix` | Flake inputs, the `mkTarget` platform × build-type generator, and `nixosConfigurations` outputs | -| `variables.nix` | Single source of truth for shared values (LAN domain/CIDR, hostnames, timezone, primary username, storage root, ...) — passed to every module and Home Manager config as the `vars` argument via `specialArgs`/`extraSpecialArgs` | +| `variables.nix` | Single source of truth for shared values (LAN domain/CIDR, hostnames, timezone, primary username, storage root, NFS share subpaths/mountpoints, service ports, ...) — passed to every module and Home Manager config as the `vars` argument via `specialArgs`/`extraSpecialArgs` | | `hosts//host.nix` | Per-machine identity: hostname, hostId, per-machine secrets, `system.stateVersion` | | `hosts/nixos/home.nix` | Workstation-specific Home Manager config (used by the `gui` build type) | | `modules/platforms/` | Platform-specific config: virtualisation guest tools, boot method, hardware config (`linode.nix`, `proxmox.nix`, `lxc.nix`) | diff --git a/hosts/nixos/home.nix b/hosts/nixos/home.nix index b083c8d..1a15cbe 100644 --- a/hosts/nixos/home.nix +++ b/hosts/nixos/home.nix @@ -31,7 +31,7 @@ [Desktop Entry] Type=Application Name=Proxmox (Chromium) - Exec=chromium --app=https://pve.${vars.homeDomain}:${toString vars.pveWebPort} --window-size=1920,1080 --window-position=0,0 + Exec=chromium --app=https://pve.${vars.homeDomain}:${toString vars.ports.pveWeb} --window-size=1920,1080 --window-position=0,0 Icon=${config.home.homeDirectory}/.local/share/icons/proxmox.png Terminal=false Categories=Hypervisor; @@ -41,7 +41,7 @@ [Desktop Entry] Type=Application Name=Proxmox Backup Server (Chromium) - Exec=chromium --app=https://${vars.pbsIp}:${toString vars.pbsWebPort} --window-size=1920,1080 --window-position=0,0 + Exec=chromium --app=https://${vars.pbsIp}:${toString vars.ports.pbsWeb} --window-size=1920,1080 --window-position=0,0 Icon=${config.home.homeDirectory}/.local/share/icons/proxmox.png Terminal=false Categories=backup; @@ -51,7 +51,7 @@ [Desktop Entry] Type=Application Name=Proxmox (Firefox) - Exec=firefox --new-instance https://pve.${vars.homeDomain}:${toString vars.pveWebPort} --profile ProxmoxWebApp --window-size=1920,1080 --class ProxmoxWebApp + Exec=firefox --new-instance https://pve.${vars.homeDomain}:${toString vars.ports.pveWeb} --profile ProxmoxWebApp --window-size=1920,1080 --class ProxmoxWebApp Icon=${config.home.homeDirectory}/.local/share/icons/proxmox.png Terminal=false Categories=Hypervisor; @@ -61,7 +61,7 @@ [Desktop Entry] Type=Application Name=Proxmox Backup Server (Firefox) - Exec=firefox --new-window https://${vars.pbsIp}:${toString vars.pbsWebPort} --profile PbsWebApp --window-size=1920,1080 --class PbsWebApp + Exec=firefox --new-window https://${vars.pbsIp}:${toString vars.ports.pbsWeb} --profile PbsWebApp --window-size=1920,1080 --class PbsWebApp Icon=${config.home.homeDirectory}/.local/share/icons/proxmox.png Terminal=false Categories=backup; diff --git a/hosts/server/host.nix b/hosts/server/host.nix index 2e9089d..ec43972 100644 --- a/hosts/server/host.nix +++ b/hosts/server/host.nix @@ -14,7 +14,7 @@ services.beszel.agent.environment = { #DOCKER_HOST = "tcp://docker-socket-proxy:2375"; KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFPR9kwtC4TAeTRu46A7+opZsYpxqkRJ+x/ZyB2GWCeG"; - EXTRA_FILESYSTEMS = "${vars.storageRoot}/docker/volumes"; + EXTRA_FILESYSTEMS = "${vars.storageRoot}/${vars.nfsShares.dockerVolumes.subpath}"; LOG_LEVEL = "debug"; }; diff --git a/modules/beszel/enable-agent.nix b/modules/beszel/enable-agent.nix index 5ebf20f..10da5ca 100644 --- a/modules/beszel/enable-agent.nix +++ b/modules/beszel/enable-agent.nix @@ -4,6 +4,6 @@ services.beszel.agent.enable = true; services.beszel.agent.environment = { #DOCKER_HOST = "tcp://docker-socket-proxy:2375"; - HUB_URL = "http://${vars.dockerHost}.${vars.homeDomain}:${toString vars.beszelHubPort}"; + HUB_URL = "http://${vars.dockerHost}.${vars.homeDomain}:${toString vars.ports.beszelHub}"; }; } diff --git a/modules/build-types/docker.nix b/modules/build-types/docker.nix index 2c4c755..36256e5 100644 --- a/modules/build-types/docker.nix +++ b/modules/build-types/docker.nix @@ -28,13 +28,18 @@ boot.supportedFilesystems = [ "nfs" ]; systemd.tmpfiles.rules = [ - "L+ /home/nixos/docker - - - - /mnt/docker/config" - "d /mnt/docker 0755 nixos users -" - "d /mnt/raspi-backup 0755 nixos users -" + "L+ /home/${vars.primaryUser}/docker - - - - ${vars.nfsShares.dockerConfig.mountpoint}" + "d /mnt/docker 0755 ${vars.primaryUser} users -" + "d ${vars.nfsShares.raspiVolumes.mountpoint} 0755 ${vars.primaryUser} users -" ]; - users.users.nixos.extraGroups = [ "docker" ]; + users.users.${vars.primaryUser}.extraGroups = [ "docker" ]; services.openssh.settings.PermitRootLogin = "yes"; - networking.firewall.allowedTCPPorts = [ 80 8080 443 vars.beszelHubPort ]; + networking.firewall.allowedTCPPorts = [ + vars.ports.dockerHttp + vars.ports.dockerExtra + vars.ports.dockerHttps + vars.ports.beszelHub + ]; } diff --git a/modules/build-types/gui.nix b/modules/build-types/gui.nix index ba20255..3c72ce7 100644 --- a/modules/build-types/gui.nix +++ b/modules/build-types/gui.nix @@ -1,4 +1,4 @@ -{ config, pkgs, lib, inputs, ... }: +{ config, pkgs, lib, inputs, vars, ... }: { environment.systemPackages = with pkgs; [ @@ -65,7 +65,7 @@ # The networkmanager group only exists when NM is actually enabled — the # lxc platform module force-disables it, so don't add the user to a group # that won't exist there. - users.users.nixos.extraGroups = lib.mkIf config.networking.networkmanager.enable [ "networkmanager" ]; + users.users.${vars.primaryUser}.extraGroups = lib.mkIf config.networking.networkmanager.enable [ "networkmanager" ]; programs.firefox.enable = true; diff --git a/modules/build-types/minimal.nix b/modules/build-types/minimal.nix index 9853475..90f91b7 100644 --- a/modules/build-types/minimal.nix +++ b/modules/build-types/minimal.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, config, ... }: +{ lib, pkgs, config, vars, ... }: { networking.networkmanager.enable = true; @@ -6,7 +6,7 @@ # The networkmanager group only exists when NM is actually enabled — the # lxc platform module force-disables it, so don't add the user to a group # that won't exist there. - users.users.nixos.extraGroups = lib.mkIf config.networking.networkmanager.enable [ "networkmanager" ]; + users.users.${vars.primaryUser}.extraGroups = lib.mkIf config.networking.networkmanager.enable [ "networkmanager" ]; environment.systemPackages = with pkgs; [ inetutils diff --git a/modules/build-types/pxe-boot.nix b/modules/build-types/pxe-boot.nix index e31deae..66c194d 100644 --- a/modules/build-types/pxe-boot.nix +++ b/modules/build-types/pxe-boot.nix @@ -155,6 +155,6 @@ in }; }; - networking.firewall.allowedTCPPorts = [ 80 ]; - networking.firewall.allowedUDPPorts = [ 69 ]; + networking.firewall.allowedTCPPorts = [ vars.ports.pxeBootHttp ]; + networking.firewall.allowedUDPPorts = [ vars.ports.pxeBootTftp ]; } diff --git a/modules/build-types/server.nix b/modules/build-types/server.nix index ba6d3b6..b1fb6d1 100644 --- a/modules/build-types/server.nix +++ b/modules/build-types/server.nix @@ -16,13 +16,13 @@ services.nfs.server = { enable = true; exports = '' - ${vars.storageRoot}/docker/config ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) - ${vars.storageRoot}/docker/volumes ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) - ${vars.storageRoot}/docker/databases ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) - ${vars.storageRoot}/docker/nextcloud-data ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) - ${vars.storageRoot}/raspi/volumes ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) + ${vars.storageRoot}/${vars.nfsShares.dockerConfig.subpath} ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) + ${vars.storageRoot}/${vars.nfsShares.dockerVolumes.subpath} ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) + ${vars.storageRoot}/${vars.nfsShares.dockerDatabases.subpath} ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) + ${vars.storageRoot}/${vars.nfsShares.nextcloudData.subpath} ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) + ${vars.storageRoot}/${vars.nfsShares.raspiVolumes.subpath} ${vars.lanCidr}(rw,sync,no_subtree_check,no_root_squash) ''; }; - networking.firewall.allowedTCPPorts = [ 111 2049 ]; + networking.firewall.allowedTCPPorts = [ vars.ports.nfsRpcbind vars.ports.nfsd ]; } diff --git a/modules/disko/proxmox.nix b/modules/disko/proxmox.nix index d2dd4c8..e6c3678 100644 --- a/modules/disko/proxmox.nix +++ b/modules/disko/proxmox.nix @@ -1,4 +1,4 @@ -{ config, ... }: +{ config, vars, ... }: { disko.devices = { @@ -13,7 +13,7 @@ # whatever's left after ESP + swap within it); imageName keeps each # host's image distinctly named instead of every proxmox-* host # producing an identical "main.raw". - imageSize = "20G"; + imageSize = vars.proxmoxImageSize; imageName = config.networking.hostName; content = { diff --git a/modules/docker/mount-data.nix b/modules/docker/mount-data.nix index 5f51873..b245a7a 100644 --- a/modules/docker/mount-data.nix +++ b/modules/docker/mount-data.nix @@ -2,8 +2,8 @@ { fileSystems = { - "/mnt/docker/config" = { - device = "${vars.nfsServerHost}:${vars.storageRoot}/docker/config"; + ${vars.nfsShares.dockerConfig.mountpoint} = { + device = "${vars.nfsServerHost}:${vars.storageRoot}/${vars.nfsShares.dockerConfig.subpath}"; fsType = "nfs"; options = [ @@ -14,8 +14,8 @@ ]; }; - "/mnt/docker/databases" = { - device = "${vars.nfsServerHost}:${vars.storageRoot}/docker/databases"; + ${vars.nfsShares.dockerDatabases.mountpoint} = { + device = "${vars.nfsServerHost}:${vars.storageRoot}/${vars.nfsShares.dockerDatabases.subpath}"; fsType = "nfs"; options = [ @@ -26,8 +26,8 @@ ]; }; - "/mnt/docker/volumes" = { - device = "${vars.nfsServerHost}:${vars.storageRoot}/docker/volumes"; + ${vars.nfsShares.dockerVolumes.mountpoint} = { + device = "${vars.nfsServerHost}:${vars.storageRoot}/${vars.nfsShares.dockerVolumes.subpath}"; fsType = "nfs"; options = [ @@ -38,8 +38,8 @@ ]; }; - "/mnt/nextcloud-data" = { - device = "${vars.nfsServerHost}:${vars.storageRoot}/docker/nextcloud-data"; + ${vars.nfsShares.nextcloudData.mountpoint} = { + device = "${vars.nfsServerHost}:${vars.storageRoot}/${vars.nfsShares.nextcloudData.subpath}"; fsType = "nfs"; options = [ @@ -50,8 +50,8 @@ ]; }; - "/mnt/raspi-backup" = { - device = "${vars.nfsServerHost}:${vars.storageRoot}/raspi/volumes"; + ${vars.nfsShares.raspiVolumes.mountpoint} = { + device = "${vars.nfsServerHost}:${vars.storageRoot}/${vars.nfsShares.raspiVolumes.subpath}"; fsType = "nfs"; options = [ diff --git a/modules/nix-cache/server.nix b/modules/nix-cache/server.nix index f4623c7..eec4609 100644 --- a/modules/nix-cache/server.nix +++ b/modules/nix-cache/server.nix @@ -28,7 +28,7 @@ openssh.enable = true; }; - networking.firewall.allowedTCPPorts = [ 80 ]; + networking.firewall.allowedTCPPorts = [ vars.ports.nixCacheHttp ]; users.groups.${vars.remoteBuilderUser} = { }; @@ -54,6 +54,6 @@ nix.gc = { automatic = true; dates = "weekly"; - options = "--delete-older-than 30d"; + options = "--delete-older-than ${vars.nixCacheGcMaxAge}"; }; } diff --git a/modules/raspi/mount-data.nix b/modules/raspi/mount-data.nix index f439bb0..857f255 100644 --- a/modules/raspi/mount-data.nix +++ b/modules/raspi/mount-data.nix @@ -1,8 +1,8 @@ { vars, ... }: { - fileSystems."/mnt/raspi" = { - device = "raspberrypi.${vars.tailnetDomain}:/home/raspi/raspi"; + fileSystems.${vars.raspiMountpoint} = { + device = "${vars.raspberryPiHost}.${vars.tailnetDomain}:${vars.raspiNfsPath}"; fsType = "nfs4"; options = [ "nofail" diff --git a/modules/traefik/rotate-logs.nix b/modules/traefik/rotate-logs.nix index 0a9968b..b676b01 100644 --- a/modules/traefik/rotate-logs.nix +++ b/modules/traefik/rotate-logs.nix @@ -1,14 +1,14 @@ -_: +{ vars, ... }: { services.logrotate = { enable = true; settings = { - "/mnt/docker/volumes/traefik-data/logs/*.log" = { + "${vars.nfsShares.dockerVolumes.mountpoint}/traefik-data/logs/*.log" = { daily = true; - size = "100M"; - rotate = 20; + size = vars.traefikLogRotate.maxSize; + rotate = vars.traefikLogRotate.keep; compress = true; missingok = true; notifempty = true; diff --git a/variables.nix b/variables.nix index 919027c..d32f073 100644 --- a/variables.nix +++ b/variables.nix @@ -12,6 +12,11 @@ nfsServerHost = "server"; # NFS export source hostname dockerHost = "docker"; # docker-compose stack host + # Raspberry Pi's own Tailscale hostname (not fronted by `server` — it + # exports its own NFS share directly). Resolved as + # "${raspberryPiHost}.${tailnetDomain}" in modules/raspi/mount-data.nix. + raspberryPiHost = "raspberrypi"; + remoteBuilderUser = "nixremote"; # remote builder SSH user # Public keys authorized to SSH in as remoteBuilderUser on the nix-cache @@ -32,13 +37,113 @@ # System timeZone = "Australia/Brisbane"; - primaryUser = "nixos"; # main interactive user on every host + + # Main interactive user on every host. Every module that grants this user + # a group, a home directory, or tmpfiles ownership should reference + # vars.primaryUser rather than the literal "nixos", so renaming it is a + # one-line change. + primaryUser = "nixos"; # Storage storageRoot = "/tank"; # ZFS pool root on `server` - # Well-known ports for LAN services referenced from multiple hosts - beszelHubPort = 8090; - pveWebPort = 8006; - pbsWebPort = 8007; + # NFS datasets exported from `storageRoot` on `nfsServerHost` and mounted + # by client hosts. `subpath` is relative to `storageRoot` — combined with + # it to build both the export line in modules/build-types/server.nix and + # the ":/" device string each client + # mount uses in modules/docker/mount-data.nix. `mountpoint` is the + # absolute local path clients mount it at, referenced by that same file's + # fileSystems attribute name plus every other place that needs to know + # where the share lives locally (modules/build-types/docker.nix's + # tmpfiles rules, modules/traefik/rotate-logs.nix's log path). Renaming a + # dataset or moving where it's mounted only needs changing it here — the + # export and every client reference follow automatically. + nfsShares = { + dockerConfig = { + subpath = "docker/config"; + mountpoint = "/mnt/docker/config"; + }; + dockerDatabases = { + subpath = "docker/databases"; + mountpoint = "/mnt/docker/databases"; + }; + dockerVolumes = { + subpath = "docker/volumes"; + mountpoint = "/mnt/docker/volumes"; + }; + nextcloudData = { + subpath = "docker/nextcloud-data"; + mountpoint = "/mnt/nextcloud-data"; + }; + raspiVolumes = { + subpath = "raspi/volumes"; + mountpoint = "/mnt/raspi-backup"; + }; + }; + + # The Raspberry Pi's own NFS export — not under storageRoot/nfsServerHost, + # served directly by the Pi itself over Tailscale (see raspberryPiHost + # above) and mounted at raspiMountpoint by modules/raspi/mount-data.nix. + raspiNfsPath = "/home/raspi/raspi"; + raspiMountpoint = "/mnt/raspi"; + + # Every literal port referenced from modules/ or hosts/, grouped by the + # service/host that opens or connects to it — kept as separate entries + # even where two happen to share a number today (e.g. nixCacheHttp and + # pxeBootHttp are both 80) so changing one service's port can never + # silently change an unrelated one. + ports = { + # nix-cache's nginx reverse proxy in front of nix-serve + # (modules/nix-cache/server.nix). + nixCacheHttp = 80; + + # pxe-boot's nginx asset server, also used to build pxeBaseUrl + # (modules/build-types/pxe-boot.nix). + pxeBootHttp = 80; + + # pxe-boot's atftpd TFTP server — UDP, not TCP + # (modules/build-types/pxe-boot.nix). + pxeBootTftp = 69; + + # `server`'s NFS exports need both the portmapper (rpcbind) and the + # NFS data port itself opened (modules/build-types/server.nix). + nfsRpcbind = 111; + nfsd = 2049; + + # Opened on the docker host's firewall for the Traefik-fronted + # container stack (docker-compose config lives in the separate + # /home/debian/docker repo, not here): 80/443 are Traefik's own + # HTTP/HTTPS listeners; 8080 is an additional exposed service whose + # exact backend isn't declared in this repo (modules/build-types/docker.nix). + dockerHttp = 80; + dockerHttps = 443; + dockerExtra = 8080; + + # Beszel monitoring hub, reachable at + # http://.: from every agent + # (modules/beszel/enable-agent.nix, hosts/nixos/home.nix). + beszelHub = 8090; + + # Proxmox VE and Proxmox Backup Server web UIs, opened as desktop + # shortcuts on the gui build type (hosts/nixos/home.nix). + pveWeb = 8006; + pbsWeb = 8007; + }; + + # .raw disk image size for every proxmox-* host's standalone Disko image + # build (modules/disko/proxmox.nix, config.system.build.diskoImagesScript + # — see docs/proxmox-images.md). Root fills whatever's left after the ESP + # and swap partitions within this total. + proxmoxImageSize = "20G"; + + # nix-cache's Nix store garbage collection retention + # (modules/nix-cache/server.nix). + nixCacheGcMaxAge = "30d"; + + # Traefik access log rotation, watched on the docker host at + # nfsShares.dockerVolumes.mountpoint (modules/traefik/rotate-logs.nix). + traefikLogRotate = { + maxSize = "100M"; # rotate once a log file exceeds this size + keep = 20; # number of rotated logs to retain before deleting the oldest + }; }