Hybrid cloud cluster monorepo with Ansible, K8s, NixOS, and Terraform. cute.haus
ansible terraform nix k8s
1

Configure Feed

Select the types of activity you want to include in your feed.

nix: major refactor

Aly Raffauf (Jun 10, 2026, 5:39 PM EDT) 692388cd 67668541

+3322 -3881
+1 -1
BACKUPS.md
··· 89 89 If we lose enough nodes to break etcd quorum, the cluster has to be rebuilt 90 90 from scratch: 91 91 92 - 1. NixOS + k3s on replacement hardware (see [`nix/hosts/README.md`](nix/hosts/README.md)). 92 + 1. NixOS + k3s on replacement hardware (see host definitions in [`nix/hosts`](nix/hosts)). 93 93 2. `helmfile sync` infra + `cluster-tls` + `pg-shared` (the `needs:` chains 94 94 resolve in correct order). 95 95 3. Restore `pg-shared` from B2 — same as the recipe above, but use
+1 -1
flake.nix
··· 69 69 systems = ["aarch64-darwin" "x86_64-linux"]; 70 70 71 71 imports = [ 72 - (inputs.import-tree ./nix/modules/flake) 72 + (inputs.import-tree ./nix) 73 73 inputs.files.flakeModules.default 74 74 inputs.flake-parts.flakeModules.modules 75 75 inputs.treefmt-nix.flakeModule
+19
nix/hardware/amd/cpu.nix
··· 1 + _: { 2 + flake.modules.nixos.amd-cpu = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + boot = { 8 + blacklistedKernelModules = ["k10temp"]; 9 + extraModulePackages = with config.boot.kernelPackages; [zenpower]; 10 + kernelModules = [ 11 + "kvm-amd" 12 + "zenpower" 13 + ]; 14 + }; 15 + 16 + hardware.cpu.amd.updateMicrocode = true; 17 + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 18 + }; 19 + }
+17
nix/hardware/amd/gpu.nix
··· 1 + _: { 2 + flake.modules.nixos.amd-gpu = { 3 + environment.variables = { 4 + DPAU_DRIVER = "radeonsi"; 5 + GSK_RENDERER = "ngl"; 6 + }; 7 + 8 + hardware = { 9 + amdgpu = { 10 + initrd.enable = true; 11 + opencl.enable = true; 12 + }; 13 + 14 + graphics.enable = true; 15 + }; 16 + }; 17 + }
+9
nix/hardware/beelink/mini/s12pro.nix
··· 1 + _: { 2 + flake.modules.nixos.beelink-mini-s12pro = { 3 + boot.initrd.availableKernelModules = [ 4 + "ahci" 5 + "sd_mod" 6 + "xhci_pci" 7 + ]; 8 + }; 9 + }
+26
nix/hardware/framework/13/intel-11th.nix
··· 1 + _: { 2 + flake.modules.nixos.framework-13-intel-11th = { 3 + lib, 4 + pkgs, 5 + ... 6 + }: { 7 + boot = { 8 + blacklistedKernelModules = ["cros-usbpd-charger"]; 9 + initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod"]; 10 + kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "6.11") (lib.mkDefault pkgs.linuxPackages_latest); 11 + 12 + kernelParams = [ 13 + "nvme.noacpi=1" 14 + ]; 15 + }; 16 + 17 + hardware.acpilight.enable = true; 18 + powerManagement.powertop.enable = lib.mkForce false; 19 + 20 + services.udev.extraRules = '' 21 + ## Framework 13 -- Fix headphone noise when on powersave 22 + ## https://community.frame.work/t/headphone-jack-intermittent-noise/5246/55 23 + SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0xa0e0", ATTR{power/control}="on" 24 + ''; 25 + }; 26 + }
+7
nix/hardware/intel/cpu.nix
··· 1 + _: { 2 + flake.modules.nixos.intel-cpu = {lib, ...}: { 3 + boot.kernelModules = ["kvm-intel"]; 4 + hardware.cpu.intel.updateMicrocode = true; 5 + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 6 + }; 7 + }
+48
nix/hardware/intel/gpu.nix
··· 1 + _: { 2 + flake.modules.nixos.intel-gpu = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: { 8 + options.myIntelGpu.driver = lib.mkOption { 9 + description = "Intel GPU driver to use"; 10 + type = lib.types.enum ["i915" "xe"]; 11 + default = "i915"; 12 + }; 13 + 14 + config = { 15 + boot.initrd.kernelModules = [config.myIntelGpu.driver]; 16 + 17 + environment.sessionVariables = { 18 + LIBVA_DRIVER_NAME = "iHD"; 19 + VDPAU_DRIVER = "va_gl"; 20 + }; 21 + 22 + hardware = { 23 + intel-gpu-tools.enable = true; 24 + 25 + graphics = { 26 + enable = true; 27 + 28 + extraPackages = [ 29 + (pkgs.intel-vaapi-driver.override {enableHybridCodec = true;}) 30 + pkgs.intel-compute-runtime 31 + pkgs.intel-media-driver 32 + pkgs.intel-ocl 33 + pkgs.libvdpau-va-gl 34 + pkgs.vpl-gpu-rt 35 + ]; 36 + 37 + extraPackages32 = [ 38 + pkgs.driversi686Linux.intel-media-driver 39 + (pkgs.driversi686Linux.intel-vaapi-driver.override {enableHybridCodec = true;}) 40 + pkgs.driversi686Linux.libvdpau-va-gl 41 + ]; 42 + }; 43 + }; 44 + 45 + services.xserver.videoDrivers = ["modesetting"]; 46 + }; 47 + }; 48 + }
+11
nix/hardware/lenovo/thinkcentre/m700.nix
··· 1 + _: { 2 + flake.modules.nixos.lenovo-thinkcentre-m700 = { 3 + boot.initrd.availableKernelModules = [ 4 + "ahci" 5 + "sd_mod" 6 + "xhci_pci" 7 + ]; 8 + 9 + services.fwupd.enable = true; 10 + }; 11 + }
+6
nix/hardware/profiles/base.nix
··· 1 + _: { 2 + flake.modules.nixos.hw-base = { 3 + hardware.enableAllFirmware = true; 4 + services.fstrim.enable = true; 5 + }; 6 + }
+56
nix/hardware/profiles/laptop.nix
··· 1 + _: { 2 + flake.modules.nixos.hw-laptop = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + boot.kernel.sysctl."kernel.nmi_watchdog" = lib.mkDefault 0; 8 + 9 + services = { 10 + tuned = { 11 + enable = lib.mkDefault true; 12 + settings.dynamic_tuning = true; 13 + }; 14 + 15 + upower.enable = true; 16 + 17 + thermald.enable = lib.mkIf (lib.elem "kvm-intel" config.boot.kernelModules) true; 18 + }; 19 + 20 + hardware.nvidia = lib.mkIf (lib.elem "nvidia" config.services.xserver.videoDrivers) { 21 + dynamicBoost.enable = lib.mkDefault true; 22 + 23 + powerManagement = { 24 + enable = true; 25 + finegrained = true; 26 + }; 27 + 28 + prime.offload = { 29 + enable = true; 30 + enableOffloadCmd = true; 31 + }; 32 + }; 33 + 34 + specialisation = lib.mkIf (lib.elem "nvidia" config.services.xserver.videoDrivers) { 35 + nvidia-sync.configuration = { 36 + environment.etc."specialisation".text = "nvidia-sync"; 37 + 38 + hardware.nvidia = { 39 + powerManagement = { 40 + enable = lib.mkForce false; 41 + finegrained = lib.mkForce false; 42 + }; 43 + 44 + prime = { 45 + offload = { 46 + enable = lib.mkForce false; 47 + enableOffloadCmd = lib.mkForce false; 48 + }; 49 + 50 + sync.enable = lib.mkForce true; 51 + }; 52 + }; 53 + }; 54 + }; 55 + }; 56 + }
-37
nix/hosts/README.md
··· 1 - # 🖥️ Hosts 2 - 3 - This directory contains the NixOS configurations for each of my devices. Each subdirectory corresponds to a specific host, encapsulating its unique setup and specifications. 4 - 5 - --- 6 - 7 - ## 📂 Directory Structure 8 - 9 - The `hosts/` directory is organized as follows: 10 - 11 - ```plaintext 12 - hosts/ 13 - ├── eterna/ # Beelink Mini S12 Pro 14 - ├── jubilife/ # Custom Mini-ITX NAS 15 - ├── pastoria/ # OVHcloud VPS 16 - ├── snowpoint/ # Netcup VPS 17 - ├── solaceon/ # Hetzner VPS 18 - └── twinleaf/ # Minimal installation ISO 19 - ``` 20 - 21 - --- 22 - 23 - ## 🛠️ Provisioning New Devices 24 - 25 - 1. **Create host configuration**: duplicate an existing host directory under `hosts/` and rename it. Update `default.nix`, `disko.nix`, `hardware.nix`, etc. to match the device. 26 - 27 - 1. **Register the host**: add it to `nixosConfigurations` in `nix/modules/flake/nixos.nix`. 28 - 29 - 1. **Install NixOS** on the device using this flake. Secrets will not decrypt on first boot until the host's age key is a recipient. 30 - 31 - 1. **Add SSH host key as a sops recipient**: copy `/etc/ssh/ssh_host_ed25519_key.pub` from the new device into `keys/root_$HOSTNAME.pub`, then run `just sops-rekey` to regenerate `.sops.yaml` and re-encrypt every secret. 32 - 33 - 1. **Rebuild** on the new device. Secrets land at `/run/secrets/` (sops-nix). 34 - 35 - 1. **(Optional) Add a user key**: drop `~/.ssh/id_ed25519.pub` from the new device into `keys/$USER_$HOSTNAME.pub` and re-run `just sops-rekey` so the user can decrypt secrets too. 36 - 37 - ---
+438
nix/hosts/eterna.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.eterna = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + base 10 + fail2ban 11 + flake-url 12 + known-hosts 13 + locale-en-us 14 + njust 15 + recipes 16 + performance 17 + ssh-keys 18 + users 19 + alloy 20 + atbbs 21 + auto-upgrade 22 + b2-mounts 23 + backups 24 + beelink-mini-s12pro 25 + btrfs 26 + caddy 27 + cachefilesd 28 + data-share 29 + hw-base 30 + intel-cpu 31 + intel-gpu 32 + k3s-node 33 + lanzaboote 34 + media-share 35 + nix-config 36 + podman 37 + prometheus-node 38 + swap 39 + syncthing 40 + tailscale 41 + vps 42 + zram 43 + 44 + inputs.disko.nixosModules.disko 45 + inputs.sops-nix.nixosModules.sops 46 + ( 47 + {config, ...}: { 48 + fileSystems."/mnt/Storage" = { 49 + device = "/dev/disk/by-id/ata-CT2000BX500SSD1_2345E8842829"; 50 + fsType = "btrfs"; 51 + options = ["compress=zstd" "noatime" "nofail"]; 52 + }; 53 + 54 + networking = { 55 + firewall = { 56 + enable = true; 57 + allowedTCPPorts = [2049]; 58 + allowedUDPPorts = [2049]; 59 + }; 60 + 61 + hostName = "eterna"; 62 + }; 63 + 64 + services.nfs.server = { 65 + enable = true; 66 + 67 + exports = '' 68 + /mnt/Storage 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=0) 69 + ''; 70 + }; 71 + 72 + powerManagement.powertop.enable = true; 73 + 74 + system.stateVersion = "25.11"; 75 + 76 + myAutoUpgrade.dates = "05:00"; 77 + 78 + myBackups.jobs = { 79 + syncthing-sync = { 80 + paths = ["/home/aly/sync"]; 81 + repository = "rclone:b2:aly-backups/syncthing/sync"; 82 + }; 83 + syncthing-roms = { 84 + paths = [config.mySyncthing.romsPath]; 85 + repository = "rclone:b2:aly-backups/syncthing/roms"; 86 + }; 87 + }; 88 + 89 + myB2Mounts = { 90 + cacheDir = "/mnt/Storage/.rclone-cache"; 91 + audioCacheSize = "10G"; 92 + audioReadAhead = "3G"; 93 + videoCacheSize = "200G"; 94 + videoReadAhead = "5G"; 95 + shares = ["Anime" "Audiobooks" "Movies" "Shows"]; 96 + }; 97 + 98 + myK3s = { 99 + role = "server"; 100 + serverAddr = "https://pastoria:6443"; 101 + zone = "home"; 102 + ingress = true; 103 + }; 104 + 105 + mySyncthing = { 106 + certFile = config.sops.secrets.syncthingCert.path; 107 + keyFile = config.sops.secrets.syncthingKey.path; 108 + user = "aly"; 109 + }; 110 + 111 + sops.secrets = { 112 + syncthingCert = { 113 + sopsFile = ../../secrets/syncthing.yaml; 114 + key = "eterna_cert"; 115 + }; 116 + syncthingKey = { 117 + sopsFile = ../../secrets/syncthing.yaml; 118 + key = "eterna_key"; 119 + }; 120 + }; 121 + 122 + myUsers.aly.password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 123 + } 124 + ) 125 + # disk layout 126 + { 127 + disko.devices = { 128 + disk = { 129 + vdb = { 130 + type = "disk"; 131 + device = "/dev/sda"; 132 + 133 + content = { 134 + type = "gpt"; 135 + 136 + partitions = { 137 + ESP = { 138 + content = { 139 + format = "vfat"; 140 + 141 + mountOptions = [ 142 + "defaults" 143 + "umask=0077" 144 + ]; 145 + 146 + mountpoint = "/boot"; 147 + type = "filesystem"; 148 + }; 149 + 150 + size = "1024M"; 151 + type = "EF00"; 152 + }; 153 + 154 + luks = { 155 + size = "100%"; 156 + 157 + content = { 158 + type = "luks"; 159 + name = "crypted"; 160 + 161 + content = { 162 + type = "btrfs"; 163 + extraArgs = ["-f"]; 164 + 165 + subvolumes = { 166 + "/root" = { 167 + mountOptions = ["compress=zstd" "noatime"]; 168 + mountpoint = "/"; 169 + }; 170 + 171 + "persist" = { 172 + mountOptions = ["compress=zstd" "noatime"]; 173 + mountpoint = "/persist"; 174 + }; 175 + 176 + "/home" = { 177 + mountOptions = ["compress=zstd" "noatime"]; 178 + mountpoint = "/home"; 179 + }; 180 + 181 + "/home/.snapshots" = { 182 + mountOptions = ["compress=zstd" "noatime"]; 183 + mountpoint = "/home/.snapshots"; 184 + }; 185 + 186 + "/nix" = { 187 + mountOptions = ["compress=zstd" "noatime"]; 188 + mountpoint = "/nix"; 189 + }; 190 + }; 191 + }; 192 + }; 193 + }; 194 + }; 195 + }; 196 + }; 197 + }; 198 + }; 199 + } 200 + # observability 201 + { 202 + services = { 203 + grafana = { 204 + enable = true; 205 + 206 + settings = { 207 + security.secret_key = "SW2YcwTIb9zpOOhoPsMm"; 208 + 209 + server = { 210 + http_addr = "0.0.0.0"; 211 + http_port = 3010; 212 + domain = "grafana.narwhal-snapper.ts.net"; 213 + }; 214 + }; 215 + 216 + provision = { 217 + enable = true; 218 + 219 + datasources.settings.datasources = [ 220 + { 221 + name = "Prometheus"; 222 + type = "prometheus"; 223 + access = "proxy"; 224 + url = "https://prometheus.narwhal-snapper.ts.net"; 225 + } 226 + { 227 + name = "Loki"; 228 + type = "loki"; 229 + access = "proxy"; 230 + url = "https://loki.narwhal-snapper.ts.net"; 231 + } 232 + ]; 233 + }; 234 + }; 235 + 236 + loki = { 237 + enable = true; 238 + 239 + configuration = { 240 + auth_enabled = false; 241 + 242 + server = { 243 + http_listen_port = 3030; 244 + grpc_listen_port = 0; 245 + }; 246 + 247 + common = { 248 + instance_addr = "0.0.0.0"; 249 + path_prefix = "/tmp/loki"; 250 + 251 + storage = { 252 + filesystem = { 253 + chunks_directory = "/tmp/loki/chunks"; 254 + rules_directory = "/tmp/loki/rules"; 255 + }; 256 + }; 257 + 258 + replication_factor = 1; 259 + 260 + ring = { 261 + kvstore = { 262 + store = "inmemory"; 263 + }; 264 + }; 265 + }; 266 + 267 + frontend = { 268 + max_outstanding_per_tenant = 2048; 269 + }; 270 + 271 + pattern_ingester = { 272 + enabled = true; 273 + }; 274 + 275 + limits_config = { 276 + max_global_streams_per_user = 0; 277 + ingestion_rate_mb = 50000; 278 + ingestion_burst_size_mb = 50000; 279 + volume_enabled = true; 280 + }; 281 + 282 + query_range = { 283 + results_cache = { 284 + cache = { 285 + embedded_cache = { 286 + enabled = true; 287 + max_size_mb = 100; 288 + }; 289 + }; 290 + }; 291 + }; 292 + 293 + schema_config = { 294 + configs = [ 295 + { 296 + from = "2020-10-24"; 297 + store = "tsdb"; 298 + object_store = "filesystem"; 299 + schema = "v13"; 300 + index = { 301 + prefix = "index_"; 302 + period = "24h"; 303 + }; 304 + } 305 + ]; 306 + }; 307 + 308 + analytics = { 309 + reporting_enabled = false; 310 + }; 311 + }; 312 + }; 313 + 314 + prometheus = { 315 + enable = true; 316 + globalConfig.scrape_interval = "10s"; 317 + port = 3020; 318 + 319 + scrapeConfigs = [ 320 + { 321 + job_name = "bazarr"; 322 + static_configs = [{targets = ["jubilife:9708"];}]; 323 + } 324 + { 325 + job_name = "lidarr"; 326 + static_configs = [{targets = ["jubilife:9709"];}]; 327 + } 328 + { 329 + job_name = "prowlarr"; 330 + static_configs = [{targets = ["jubilife:9710"];}]; 331 + } 332 + { 333 + job_name = "radarr"; 334 + static_configs = [{targets = ["jubilife:9711"];}]; 335 + } 336 + { 337 + job_name = "smartctl"; 338 + static_configs = [ 339 + { 340 + targets = ["jubilife:9633"]; 341 + labels.instance = "jubilife"; 342 + } 343 + ]; 344 + } 345 + { 346 + job_name = "sonarr"; 347 + static_configs = [{targets = ["jubilife:9712"];}]; 348 + } 349 + { 350 + job_name = "node"; 351 + static_configs = [ 352 + { 353 + targets = ["snowpoint:3021"]; 354 + labels.instance = "snowpoint"; 355 + } 356 + { 357 + targets = ["pastoria:3021"]; 358 + labels.instance = "pastoria"; 359 + } 360 + { 361 + targets = ["jubilife:3021"]; 362 + labels.instance = "jubilife"; 363 + } 364 + { 365 + targets = ["eterna:3021"]; 366 + labels.instance = "eterna"; 367 + } 368 + { 369 + targets = ["solaceon:3021"]; 370 + labels.instance = "solaceon"; 371 + } 372 + ]; 373 + } 374 + ]; 375 + }; 376 + }; 377 + } 378 + # services 379 + (let 380 + tnet = "narwhal-snapper.ts.net"; 381 + in { 382 + services = { 383 + caddy = { 384 + email = "alyraffauf@fastmail.com"; 385 + virtualHosts = { 386 + "grafana.${tnet}".extraConfig = '' 387 + bind tailscale/grafana 388 + encode zstd gzip 389 + reverse_proxy eterna:3010 390 + ''; 391 + 392 + "loki.${tnet}".extraConfig = '' 393 + bind tailscale/loki 394 + encode zstd gzip 395 + reverse_proxy eterna:3030 396 + ''; 397 + 398 + "prometheus.${tnet}".extraConfig = '' 399 + bind tailscale/prometheus 400 + encode zstd gzip 401 + reverse_proxy eterna:3020 402 + ''; 403 + }; 404 + }; 405 + 406 + karakeep = { 407 + enable = false; 408 + 409 + extraEnvironment = rec { 410 + DISABLE_NEW_RELEASE_CHECK = "true"; 411 + DISABLE_SIGNUPS = "true"; 412 + INFERENCE_CONTEXT_LENGTH = "128000"; 413 + INFERENCE_EMBEDDING_MODEL = "nomic-embed-text"; 414 + INFERENCE_ENABLE_AUTO_SUMMARIZATION = "true"; 415 + INFERENCE_IMAGE_MODEL = "gemma3:4b"; 416 + INFERENCE_JOB_TIMEOUT_SEC = "600"; 417 + INFERENCE_LANG = "english"; 418 + INFERENCE_TEXT_MODEL = INFERENCE_IMAGE_MODEL; 419 + NEXTAUTH_URL = "https://karakeep.cute.haus"; 420 + OLLAMA_BASE_URL = "https://ollama.${tnet}"; 421 + OLLAMA_KEEP_ALIVE = "5m"; 422 + PORT = "7020"; 423 + }; 424 + }; 425 + 426 + meilisearch.settings.experimental_dumpless_upgrade = true; 427 + }; 428 + }) 429 + 430 + { 431 + nixpkgs = { 432 + overlays = [self.overlays.default]; 433 + config.allowUnfree = true; 434 + }; 435 + } 436 + ]; 437 + }; 438 + }
-13
nix/hosts/eterna/README.md
··· 1 - # 🖥️ Eterna 2 - 3 - ## 💻 Specifications 4 - 5 - | Component | Details | 6 - | ---------- | ------------------------- | 7 - | **Model** | Beelink Mini S12 Pro | 8 - | **CPU** | Intel N100 | 9 - | **RAM** | 16GB (2x8GB) DDR4-3200MHz | 10 - | **Disk 1** | 512GB M.2 SATA SSD | 11 - | **Disk 2** | 4TB SATA SSD | 12 - 13 - ---
-120
nix/hosts/eterna/default.nix
··· 1 - { 2 - config, 3 - self, 4 - ... 5 - }: { 6 - fileSystems."/mnt/Storage" = { 7 - device = "/dev/disk/by-id/ata-CT2000BX500SSD1_2345E8842829"; 8 - fsType = "btrfs"; 9 - options = ["compress=zstd" "noatime" "nofail"]; 10 - }; 11 - 12 - networking = { 13 - firewall = { 14 - enable = true; 15 - allowedTCPPorts = [2049]; # NFS 16 - allowedUDPPorts = [2049]; 17 - }; 18 - 19 - hostName = "eterna"; 20 - }; 21 - 22 - services.nfs.server = { 23 - enable = true; 24 - 25 - exports = '' 26 - /mnt/Storage 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=0) 27 - ''; 28 - }; 29 - 30 - powerManagement.powertop.enable = true; 31 - 32 - system.stateVersion = "25.11"; 33 - myHardware.beelink.mini.s12pro.enable = true; 34 - 35 - myNixOS = { 36 - base.enable = true; 37 - 38 - profiles = { 39 - autoUpgrade = { 40 - enable = true; 41 - dates = "05:00"; 42 - }; 43 - 44 - backups = { 45 - enable = true; 46 - extraJobs.syncthing-sync = { 47 - paths = ["/home/aly/sync"]; 48 - repository = "rclone:b2:aly-backups/syncthing/sync"; 49 - }; 50 - extraJobs.syncthing-roms = { 51 - paths = [config.myNixOS.services.syncthing.romsPath]; 52 - repository = "rclone:b2:aly-backups/syncthing/roms"; 53 - }; 54 - }; 55 - btrfs.enable = true; 56 - 57 - b2-mounts = { 58 - enable = true; 59 - cacheDir = "/mnt/Storage/.rclone-cache"; 60 - audioCacheSize = "10G"; 61 - audioReadAhead = "3G"; 62 - videoCacheSize = "200G"; 63 - videoReadAhead = "5G"; 64 - shares = ["Anime" "Audiobooks" "Movies" "Shows"]; 65 - }; 66 - 67 - data-share.enable = true; 68 - 69 - k3s = { 70 - enable = true; 71 - role = "server"; 72 - serverAddr = "https://pastoria:6443"; 73 - zone = "home"; 74 - ingress = true; 75 - }; 76 - 77 - media-share.enable = true; 78 - vps.enable = true; 79 - swap.enable = true; 80 - zram.enable = true; 81 - }; 82 - 83 - programs = { 84 - lanzaboote.enable = true; 85 - nix.enable = true; 86 - podman.enable = true; 87 - }; 88 - 89 - services = { 90 - caddy.enable = true; 91 - prometheusNode.enable = true; 92 - alloy.enable = true; 93 - 94 - syncthing = { 95 - enable = true; 96 - certFile = config.sops.secrets.syncthingCert.path; 97 - keyFile = config.sops.secrets.syncthingKey.path; 98 - user = "aly"; 99 - }; 100 - 101 - tailscale.enable = true; 102 - }; 103 - }; 104 - 105 - sops.secrets = { 106 - syncthingCert = { 107 - sopsFile = "${self}/secrets/syncthing.yaml"; 108 - key = "eterna_cert"; 109 - }; 110 - syncthingKey = { 111 - sopsFile = "${self}/secrets/syncthing.yaml"; 112 - key = "eterna_key"; 113 - }; 114 - }; 115 - 116 - myUsers.aly = { 117 - enable = true; 118 - password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 119 - }; 120 - }
-74
nix/hosts/eterna/disko.nix
··· 1 - { 2 - disko.devices = { 3 - disk = { 4 - vdb = { 5 - type = "disk"; 6 - device = "/dev/sda"; 7 - 8 - content = { 9 - type = "gpt"; 10 - 11 - partitions = { 12 - ESP = { 13 - content = { 14 - format = "vfat"; 15 - 16 - mountOptions = [ 17 - "defaults" 18 - "umask=0077" 19 - ]; 20 - 21 - mountpoint = "/boot"; 22 - type = "filesystem"; 23 - }; 24 - 25 - size = "1024M"; 26 - type = "EF00"; 27 - }; 28 - 29 - luks = { 30 - size = "100%"; 31 - 32 - content = { 33 - type = "luks"; 34 - name = "crypted"; 35 - 36 - content = { 37 - type = "btrfs"; 38 - extraArgs = ["-f"]; 39 - 40 - subvolumes = { 41 - "/root" = { 42 - mountOptions = ["compress=zstd" "noatime"]; 43 - mountpoint = "/"; 44 - }; 45 - 46 - "persist" = { 47 - mountOptions = ["compress=zstd" "noatime"]; 48 - mountpoint = "/persist"; 49 - }; 50 - 51 - "/home" = { 52 - mountOptions = ["compress=zstd" "noatime"]; 53 - mountpoint = "/home"; 54 - }; 55 - 56 - "/home/.snapshots" = { 57 - mountOptions = ["compress=zstd" "noatime"]; 58 - mountpoint = "/home/.snapshots"; 59 - }; 60 - 61 - "/nix" = { 62 - mountOptions = ["compress=zstd" "noatime"]; 63 - mountpoint = "/nix"; 64 - }; 65 - }; 66 - }; 67 - }; 68 - }; 69 - }; 70 - }; 71 - }; 72 - }; 73 - }; 74 - }
-203
nix/hosts/eterna/grafana.nix
··· 1 - _: { 2 - services = { 3 - grafana = { 4 - enable = true; 5 - 6 - settings = { 7 - security.secret_key = "SW2YcwTIb9zpOOhoPsMm"; 8 - 9 - server = { 10 - http_addr = "0.0.0.0"; 11 - http_port = 3010; 12 - domain = "grafana.narwhal-snapper.ts.net"; 13 - }; 14 - }; 15 - 16 - provision = { 17 - enable = true; 18 - 19 - datasources.settings.datasources = [ 20 - { 21 - name = "Prometheus"; 22 - type = "prometheus"; 23 - access = "proxy"; 24 - url = "https://prometheus.narwhal-snapper.ts.net"; 25 - } 26 - { 27 - name = "Loki"; 28 - type = "loki"; 29 - access = "proxy"; 30 - url = "https://loki.narwhal-snapper.ts.net"; 31 - } 32 - ]; 33 - }; 34 - }; 35 - 36 - loki = { 37 - enable = true; 38 - 39 - configuration = { 40 - auth_enabled = false; 41 - 42 - server = { 43 - http_listen_port = 3030; 44 - grpc_listen_port = 0; 45 - }; 46 - 47 - common = { 48 - instance_addr = "0.0.0.0"; 49 - path_prefix = "/tmp/loki"; 50 - 51 - storage = { 52 - filesystem = { 53 - chunks_directory = "/tmp/loki/chunks"; 54 - rules_directory = "/tmp/loki/rules"; 55 - }; 56 - }; 57 - 58 - replication_factor = 1; 59 - 60 - ring = { 61 - kvstore = { 62 - store = "inmemory"; 63 - }; 64 - }; 65 - }; 66 - 67 - frontend = { 68 - max_outstanding_per_tenant = 2048; 69 - }; 70 - 71 - pattern_ingester = { 72 - enabled = true; 73 - }; 74 - 75 - limits_config = { 76 - max_global_streams_per_user = 0; 77 - ingestion_rate_mb = 50000; 78 - ingestion_burst_size_mb = 50000; 79 - volume_enabled = true; 80 - }; 81 - 82 - query_range = { 83 - results_cache = { 84 - cache = { 85 - embedded_cache = { 86 - enabled = true; 87 - max_size_mb = 100; 88 - }; 89 - }; 90 - }; 91 - }; 92 - 93 - schema_config = { 94 - configs = [ 95 - { 96 - from = "2020-10-24"; 97 - store = "tsdb"; 98 - object_store = "filesystem"; 99 - schema = "v13"; 100 - index = { 101 - prefix = "index_"; 102 - period = "24h"; 103 - }; 104 - } 105 - ]; 106 - }; 107 - 108 - analytics = { 109 - reporting_enabled = false; 110 - }; 111 - }; 112 - }; 113 - 114 - prometheus = { 115 - enable = true; 116 - globalConfig.scrape_interval = "10s"; 117 - port = 3020; 118 - 119 - scrapeConfigs = [ 120 - { 121 - job_name = "bazarr"; 122 - static_configs = [ 123 - { 124 - targets = ["jubilife:9708"]; 125 - } 126 - ]; 127 - } 128 - 129 - { 130 - job_name = "lidarr"; 131 - static_configs = [ 132 - { 133 - targets = ["jubilife:9709"]; 134 - } 135 - ]; 136 - } 137 - 138 - { 139 - job_name = "prowlarr"; 140 - static_configs = [ 141 - { 142 - targets = ["jubilife:9710"]; 143 - } 144 - ]; 145 - } 146 - 147 - { 148 - job_name = "radarr"; 149 - static_configs = [ 150 - { 151 - targets = ["jubilife:9711"]; 152 - } 153 - ]; 154 - } 155 - 156 - { 157 - job_name = "smartctl"; 158 - static_configs = [ 159 - { 160 - targets = ["jubilife:9633"]; 161 - labels.instance = "jubilife"; 162 - } 163 - ]; 164 - } 165 - 166 - { 167 - job_name = "sonarr"; 168 - static_configs = [ 169 - { 170 - targets = ["jubilife:9712"]; 171 - } 172 - ]; 173 - } 174 - 175 - { 176 - job_name = "node"; 177 - static_configs = [ 178 - { 179 - targets = ["snowpoint:3021"]; 180 - labels.instance = "snowpoint"; 181 - } 182 - { 183 - targets = ["pastoria:3021"]; 184 - labels.instance = "pastoria"; 185 - } 186 - { 187 - targets = ["jubilife:3021"]; 188 - labels.instance = "jubilife"; 189 - } 190 - { 191 - targets = ["eterna:3021"]; 192 - labels.instance = "eterna"; 193 - } 194 - { 195 - targets = ["solaceon:3021"]; 196 - labels.instance = "solaceon"; 197 - } 198 - ]; 199 - } 200 - ]; 201 - }; 202 - }; 203 - }
-54
nix/hosts/eterna/services.nix
··· 1 - _: let 2 - tnet = "narwhal-snapper.ts.net"; 3 - in { 4 - myNixOS.services = { 5 - atbbs.enable = true; 6 - }; 7 - 8 - services = { 9 - caddy = { 10 - email = "alyraffauf@fastmail.com"; 11 - virtualHosts = { 12 - "grafana.${tnet}".extraConfig = '' 13 - bind tailscale/grafana 14 - encode zstd gzip 15 - reverse_proxy eterna:3010 16 - ''; 17 - 18 - "loki.${tnet}".extraConfig = '' 19 - bind tailscale/loki 20 - encode zstd gzip 21 - reverse_proxy eterna:3030 22 - ''; 23 - 24 - "prometheus.${tnet}".extraConfig = '' 25 - bind tailscale/prometheus 26 - encode zstd gzip 27 - reverse_proxy eterna:3020 28 - ''; 29 - }; 30 - }; 31 - 32 - karakeep = { 33 - enable = false; 34 - 35 - extraEnvironment = rec { 36 - DISABLE_NEW_RELEASE_CHECK = "true"; 37 - DISABLE_SIGNUPS = "true"; 38 - INFERENCE_CONTEXT_LENGTH = "128000"; 39 - INFERENCE_EMBEDDING_MODEL = "nomic-embed-text"; 40 - INFERENCE_ENABLE_AUTO_SUMMARIZATION = "true"; 41 - INFERENCE_IMAGE_MODEL = "gemma3:4b"; 42 - INFERENCE_JOB_TIMEOUT_SEC = "600"; 43 - INFERENCE_LANG = "english"; 44 - INFERENCE_TEXT_MODEL = INFERENCE_IMAGE_MODEL; 45 - NEXTAUTH_URL = "https://karakeep.cute.haus"; 46 - OLLAMA_BASE_URL = "https://ollama.${tnet}"; 47 - OLLAMA_KEEP_ALIVE = "5m"; 48 - PORT = "7020"; 49 - }; 50 - }; 51 - 52 - meilisearch.settings.experimental_dumpless_upgrade = true; 53 - }; 54 - }
+538
nix/hosts/jubilife.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.jubilife = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + base 10 + fail2ban 11 + flake-url 12 + known-hosts 13 + locale-en-us 14 + njust 15 + recipes 16 + performance 17 + ssh-keys 18 + users 19 + alloy 20 + amd-cpu 21 + arr 22 + auto-upgrade 23 + b2-mounts 24 + backups 25 + btrfs 26 + caddy 27 + forgejo-runner 28 + hw-base 29 + intel-gpu 30 + k3s-node 31 + lanzaboote 32 + nix-config 33 + plex 34 + podman 35 + prometheus-node 36 + qbittorrent 37 + swap 38 + syncthing 39 + tailscale 40 + tautulli 41 + vps 42 + zram 43 + 44 + inputs.disko.nixosModules.disko 45 + inputs.sops-nix.nixosModules.sops 46 + config.flake.diskoConfigurations.luks-btrfs-subvolumes 47 + ( 48 + { 49 + config, 50 + lib, 51 + pkgs, 52 + ... 53 + }: let 54 + dataDirectory = "/mnt/Data"; 55 + in { 56 + boot = { 57 + initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" "r8169"]; 58 + kernelModules = ["sg"]; 59 + }; 60 + 61 + environment.systemPackages = with pkgs; [ 62 + abcde 63 + chezmoi 64 + claude-code 65 + curl 66 + delta 67 + eza 68 + ffmpeg-full 69 + flac 70 + fzf 71 + gh 72 + handbrake 73 + lazygit 74 + makemkv 75 + mediainfo 76 + mkvtoolnix 77 + opencode 78 + rclone 79 + restic 80 + ripgrep 81 + starship 82 + zoxide 83 + ]; 84 + 85 + fileSystems = { 86 + "/mnt/Data" = { 87 + device = "/dev/disk/by-id/ata-CT4000BX500SSD1_2447E9959972"; 88 + fsType = "btrfs"; 89 + options = ["compress=zstd" "noatime" "nofail"]; 90 + }; 91 + 92 + "/mnt/Media" = { 93 + device = "/dev/disk/by-id/ata-ST14000NM001G-2KJ103_ZL201XNJ-part1"; 94 + fsType = "btrfs"; 95 + options = ["subvol=@media" "compress=zstd" "noatime" "nofail"]; 96 + }; 97 + }; 98 + 99 + networking = { 100 + firewall.allowedTCPPorts = [2342 5143 6881]; 101 + hostName = "jubilife"; 102 + }; 103 + 104 + services.udev.extraRules = let 105 + mkRule = as: lib.concatStringsSep ", " as; 106 + mkRules = rs: lib.concatStringsSep "\n" rs; 107 + in 108 + mkRules [ 109 + (mkRule [ 110 + ''ACTION=="add|change"'' 111 + ''SUBSYSTEM=="block"'' 112 + ''KERNEL=="sd[a-z]"'' 113 + ''ATTR{queue/rotational}=="1"'' 114 + ''RUN+="${pkgs.hdparm}/bin/hdparm -B 90 -S 41 /dev/%k"'' 115 + ]) 116 + ]; 117 + 118 + system.stateVersion = "25.11"; 119 + myDisko.installDrive = "/dev/disk/by-id/nvme-PNY_CS2130_1TB_SSD_PNY211821050701050CC"; 120 + 121 + myArr.dataDir = "/mnt/Data"; 122 + myAutoUpgrade.dates = "04:15"; 123 + 124 + myB2Mounts = { 125 + cacheDir = "/mnt/Data/.rclone-cache"; 126 + audioCacheSize = "50G"; 127 + audioReadAhead = "3G"; 128 + videoCacheSize = "300G"; 129 + videoReadAhead = "5G"; 130 + }; 131 + 132 + myForgejoRunner = { 133 + dockerContainers = 3; 134 + nativeRunners = 2; 135 + }; 136 + 137 + myK3s = { 138 + role = "agent"; 139 + serverAddr = "https://pastoria:6443"; 140 + zone = "home"; 141 + }; 142 + 143 + myPlex.dataDir = "/mnt/Data"; 144 + 145 + myQbittorrent.port = 8080; 146 + 147 + mySyncthing = { 148 + certFile = config.sops.secrets.syncthingCert.path; 149 + keyFile = config.sops.secrets.syncthingKey.path; 150 + romsPath = "${dataDirectory}/syncthing/ROMs"; 151 + syncROMs = true; 152 + user = "aly"; 153 + }; 154 + 155 + sops.secrets = { 156 + syncthingCert = { 157 + sopsFile = ../../secrets/syncthing.yaml; 158 + key = "jubilife_cert"; 159 + }; 160 + syncthingKey = { 161 + sopsFile = ../../secrets/syncthing.yaml; 162 + key = "jubilife_key"; 163 + }; 164 + }; 165 + 166 + myUsers.aly.password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 167 + } 168 + ) 169 + # containers 170 + { 171 + myBackups.jobs.dizquetv.paths = ["/mnt/Data/dizquetv"]; 172 + 173 + systemd.tmpfiles.rules = [ 174 + "d /mnt/Data/dizquetv 0755 root root" 175 + "d /mnt/Data/arm/home 0755 1000 1000 - -" 176 + "d /mnt/Data/arm/config 0755 1000 1000 - -" 177 + "d /mnt/Data/arm 0755 1000 1000 - -" 178 + ]; 179 + 180 + virtualisation.oci-containers.containers = { 181 + # arm = { 182 + # autoStart = true; 183 + # image = "automaticrippingmachine/automatic-ripping-machine:latest"; 184 + # ports = ["8181:8080"]; 185 + 186 + # volumes = [ 187 + # "/mnt/Data/arm/home:/home/arm" 188 + # "/mnt/Data/arm/config:/etc/arm/config" 189 + # ]; 190 + 191 + # extraOptions = [ 192 + # # Needed for ARM to work correctly - by default `CAP_SYS_ADMIN` is dropped 193 + # # which blocks `mount()` calls within the container 194 + # # This is needed in order to `mount /dev/sr0 /mnt/dev/sr0` for ripping, which may be avoidable by 195 + # # handling mounts outside of the container, and having `/mnt/dev` bind mounted into the container. 196 + # "--privileged" 197 + # # Pass the CD/Bluray/DVD drive to the container 198 + # "--device=/dev/sr0:/dev/sr0" 199 + # "--pull=always" 200 + # ]; 201 + # }; 202 + 203 + dizquetv = { 204 + image = "vexorian/dizquetv:latest"; 205 + extraOptions = ["--pull=always"]; 206 + ports = ["0.0.0.0:8000:8000"]; 207 + 208 + volumes = [ 209 + "/mnt/Data/dizquetv:/home/node/app/.dizquetv" 210 + "/etc/localtime:/etc/localtime:ro" 211 + ]; 212 + }; 213 + }; 214 + } 215 + # prometheus exporters 216 + ( 217 + {config, ...}: { 218 + sops.secrets = { 219 + bazarrApiKey = { 220 + sopsFile = ../../secrets/arr.yaml; 221 + key = "bazarr_api_key"; 222 + }; 223 + lidarrApiKey = { 224 + sopsFile = ../../secrets/arr.yaml; 225 + key = "lidarr_api_key"; 226 + }; 227 + prowlarrApiKey = { 228 + sopsFile = ../../secrets/arr.yaml; 229 + key = "prowlarr_api_key"; 230 + }; 231 + radarrApiKey = { 232 + sopsFile = ../../secrets/arr.yaml; 233 + key = "radarr_api_key"; 234 + }; 235 + sonarrApiKey = { 236 + sopsFile = ../../secrets/arr.yaml; 237 + key = "sonarr_api_key"; 238 + }; 239 + }; 240 + 241 + services.prometheus.exporters = { 242 + exportarr-bazarr = { 243 + enable = true; 244 + apiKeyFile = config.sops.secrets.bazarrApiKey.path; 245 + port = 9708; 246 + url = "https://bazarr.narwhal-snapper.ts.net"; 247 + }; 248 + 249 + exportarr-lidarr = { 250 + enable = true; 251 + apiKeyFile = config.sops.secrets.lidarrApiKey.path; 252 + port = 9709; 253 + url = "https://lidarr.narwhal-snapper.ts.net"; 254 + }; 255 + 256 + exportarr-prowlarr = { 257 + enable = true; 258 + apiKeyFile = config.sops.secrets.prowlarrApiKey.path; 259 + port = 9710; 260 + url = "https://prowlarr.narwhal-snapper.ts.net"; 261 + }; 262 + 263 + exportarr-radarr = { 264 + enable = true; 265 + apiKeyFile = config.sops.secrets.radarrApiKey.path; 266 + port = 9711; 267 + url = "https://radarr.narwhal-snapper.ts.net"; 268 + }; 269 + 270 + exportarr-sonarr = { 271 + enable = true; 272 + apiKeyFile = config.sops.secrets.sonarrApiKey.path; 273 + port = 9712; 274 + url = "https://sonarr.narwhal-snapper.ts.net"; 275 + }; 276 + 277 + smartctl.enable = true; 278 + }; 279 + } 280 + ) 281 + # services 282 + ( 283 + { 284 + config, 285 + pkgs, 286 + ... 287 + }: let 288 + dataDirectory = "/mnt/Data"; 289 + tnet = "narwhal-snapper.ts.net"; 290 + stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 291 + start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 292 + in { 293 + myBackups.jobs = { 294 + immich = { 295 + backupCleanupCommand = start "immich-server"; 296 + backupPrepareCommand = stop "immich-server"; 297 + paths = [ 298 + "${config.services.immich.mediaLocation}/library" 299 + "${config.services.immich.mediaLocation}/profile" 300 + "${config.services.immich.mediaLocation}/upload" 301 + "${config.services.immich.mediaLocation}/backups" 302 + ]; 303 + }; 304 + 305 + jellyfin = { 306 + backupCleanupCommand = start "jellyfin"; 307 + backupPrepareCommand = stop "jellyfin"; 308 + paths = [config.services.jellyfin.dataDir]; 309 + }; 310 + }; 311 + 312 + sops = { 313 + templates."immich-config.json" = { 314 + owner = "immich"; 315 + content = '' 316 + { 317 + "oauth": { 318 + "enabled": true, 319 + "issuerUrl": "https://id.cute.haus", 320 + "clientId": "${config.sops.placeholder.immichOauthClientId}", 321 + "clientSecret": "${config.sops.placeholder.immichOauthClientSecret}", 322 + "scope": "openid email profile", 323 + "buttonText": "Sign in with cute.haus", 324 + "autoRegister": true, 325 + "autoLaunch": false, 326 + "mobileOverrideEnabled": false, 327 + "mobileRedirectUri": "" 328 + } 329 + } 330 + ''; 331 + }; 332 + 333 + secrets = { 334 + immichOauthClientId = { 335 + sopsFile = ../../secrets/immich.yaml; 336 + key = "oauth/client_id"; 337 + owner = "immich"; 338 + }; 339 + immichOauthClientSecret = { 340 + sopsFile = ../../secrets/immich.yaml; 341 + key = "oauth/client_secret"; 342 + owner = "immich"; 343 + }; 344 + photoprismAdminPass = { 345 + sopsFile = ../../secrets/photoprism.yaml; 346 + key = "ADMIN_PASSWORD"; 347 + }; 348 + }; 349 + }; 350 + 351 + networking.firewall.allowedTCPPorts = [6881]; 352 + 353 + services = { 354 + caddy.virtualHosts = { 355 + "bazarr.${tnet}".extraConfig = '' 356 + bind tailscale/bazarr 357 + encode zstd gzip 358 + reverse_proxy jubilife:6767 359 + ''; 360 + 361 + "jellyfin.${tnet}".extraConfig = '' 362 + bind tailscale/jellyfin 363 + encode zstd gzip 364 + reverse_proxy jubilife:8096 { 365 + flush_interval -1 366 + } 367 + ''; 368 + 369 + "lidarr.${tnet}".extraConfig = '' 370 + bind tailscale/lidarr 371 + encode zstd gzip 372 + reverse_proxy jubilife:8686 373 + ''; 374 + 375 + "navidrome.${tnet}".extraConfig = '' 376 + bind tailscale/navidrome 377 + encode zstd gzip 378 + reverse_proxy snowpoint:4533 379 + ''; 380 + 381 + "ollama.${tnet}".extraConfig = '' 382 + bind tailscale/ollama 383 + encode zstd gzip 384 + reverse_proxy jubilife:11434 385 + ''; 386 + 387 + "prowlarr.${tnet}".extraConfig = '' 388 + bind tailscale/prowlarr 389 + encode zstd gzip 390 + reverse_proxy jubilife:9696 391 + ''; 392 + 393 + "qbittorrent.${tnet}".extraConfig = '' 394 + bind tailscale/qbittorrent 395 + encode zstd gzip 396 + reverse_proxy jubilife:8080 397 + ''; 398 + 399 + "radarr.${tnet}".extraConfig = '' 400 + bind tailscale/radarr 401 + encode zstd gzip 402 + reverse_proxy jubilife:7878 403 + ''; 404 + 405 + "sonarr.${tnet}".extraConfig = '' 406 + bind tailscale/sonarr 407 + encode zstd gzip 408 + reverse_proxy jubilife:8989 409 + ''; 410 + 411 + "tautulli.${tnet}".extraConfig = '' 412 + bind tailscale/tautulli 413 + encode zstd gzip 414 + reverse_proxy jubilife:8181 415 + ''; 416 + }; 417 + 418 + immich = { 419 + enable = true; 420 + host = "0.0.0.0"; 421 + mediaLocation = "${dataDirectory}/immich"; 422 + openFirewall = true; 423 + port = 2283; 424 + environment.IMMICH_CONFIG_FILE = config.sops.templates."immich-config.json".path; 425 + }; 426 + 427 + jellyfin = { 428 + enable = true; 429 + openFirewall = true; 430 + dataDir = "${dataDirectory}/jellyfin"; 431 + }; 432 + 433 + nfs.server = { 434 + enable = true; 435 + exports = '' 436 + /mnt/Data 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=0) 437 + /mnt/Media 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=1) 438 + ''; 439 + }; 440 + 441 + ollama = { 442 + enable = true; 443 + host = "0.0.0.0"; 444 + 445 + loadModels = [ 446 + "gemma3:12b" 447 + "gemma3:4b" 448 + "nomic-embed-text" 449 + ]; 450 + 451 + openFirewall = true; 452 + }; 453 + 454 + samba = { 455 + enable = true; 456 + openFirewall = true; 457 + 458 + settings = { 459 + global = { 460 + security = "user"; 461 + "map to guest" = "Bad User"; 462 + 463 + # Protocol tuning 464 + "server min protocol" = "SMB3"; 465 + "server max protocol" = "SMB3_11"; 466 + 467 + # Performance options 468 + "socket options" = "TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=262144 SO_SNDBUF=262144"; 469 + "use sendfile" = "no"; # Plex compatibility 470 + "aio read size" = "1"; 471 + "aio write size" = "1"; 472 + "min receivefile size" = "131072"; # Bump slightly from 16K to 128K 473 + "max xmit" = "65535"; # Samba's max recommended for best throughput 474 + 475 + # Locking & latency 476 + "strict locking" = "no"; 477 + "oplocks" = "yes"; 478 + "level2 oplocks" = "yes"; 479 + }; 480 + 481 + Data = { 482 + "create mask" = "0755"; 483 + "directory mask" = "0755"; 484 + "force group" = "users"; 485 + "force user" = "aly"; 486 + "guest ok" = "yes"; 487 + "read only" = "no"; 488 + browseable = "yes"; 489 + comment = "Data @ ${config.networking.hostName}"; 490 + path = dataDirectory; 491 + }; 492 + 493 + Media = { 494 + "create mask" = "0755"; 495 + "directory mask" = "0755"; 496 + "force group" = "users"; 497 + "force user" = "aly"; 498 + "guest ok" = "yes"; 499 + "read only" = "no"; 500 + browseable = "yes"; 501 + comment = "Media @ ${config.networking.hostName}"; 502 + path = "/mnt/Media"; 503 + }; 504 + }; 505 + }; 506 + 507 + samba-wsdd = { 508 + enable = true; 509 + openFirewall = true; 510 + }; 511 + 512 + smartd.enable = true; 513 + 514 + snapper.configs.media = { 515 + ALLOW_GROUPS = ["users"]; 516 + FSTYPE = "btrfs"; 517 + SUBVOLUME = "/mnt/Media"; 518 + TIMELINE_CLEANUP = true; 519 + TIMELINE_CREATE = true; 520 + }; 521 + 522 + tuned = { 523 + enable = true; 524 + settings.dynamic_tuning = true; 525 + }; 526 + }; 527 + } 528 + ) 529 + 530 + { 531 + nixpkgs = { 532 + overlays = [self.overlays.default]; 533 + config.allowUnfree = true; 534 + }; 535 + } 536 + ]; 537 + }; 538 + }
-15
nix/hosts/jubilife/README.md
··· 1 - # 🎮 Jubilife 2 - 3 - ## 💻 Specifications 4 - 5 - | Component | Details | 6 - | ---------- | -------------------------- | 7 - | **Model** | Custom Mini-ITX NAS | 8 - | **CPU** | AMD Ryzen 5 5600G | 9 - | **RAM** | 32GB (2x16GB) DDR4-3200MHz | 10 - | **GPU** | AMD Radeon RX 6700 | 11 - | **Disk 1** | 1TB M.2 PCIe 3.0 SSD | 12 - | **Disk 2** | 2TB SATA HDD | 13 - | **Disk 3** | 2x14TB SATA HDD in RAID | 14 - 15 - ---
-189
nix/hosts/jubilife/default.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: let 8 - dataDirectory = "/mnt/Data"; 9 - in { 10 - imports = [ 11 - self.diskoConfigurations.luks-btrfs-subvolumes 12 - ]; 13 - 14 - boot = { 15 - initrd.availableKernelModules = ["xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" "r8169"]; 16 - kernelModules = ["sg"]; 17 - }; 18 - 19 - environment.systemPackages = with pkgs; [ 20 - abcde 21 - chezmoi 22 - claude-code 23 - curl 24 - delta 25 - eza 26 - ffmpeg-full 27 - flac 28 - fzf 29 - gh 30 - handbrake 31 - lazygit 32 - makemkv 33 - mediainfo 34 - mkvtoolnix 35 - opencode 36 - rclone 37 - restic 38 - ripgrep 39 - starship 40 - zoxide 41 - ]; 42 - 43 - fileSystems = { 44 - "/mnt/Data" = { 45 - device = "/dev/disk/by-id/ata-CT4000BX500SSD1_2447E9959972"; 46 - fsType = "btrfs"; 47 - options = ["compress=zstd" "noatime" "nofail"]; 48 - }; 49 - 50 - "/mnt/Media" = { 51 - device = "/dev/disk/by-id/ata-ST14000NM001G-2KJ103_ZL201XNJ-part1"; 52 - fsType = "btrfs"; 53 - options = ["subvol=@media" "compress=zstd" "noatime" "nofail"]; 54 - }; 55 - }; 56 - 57 - networking = { 58 - firewall.allowedTCPPorts = [2342 5143 6881]; 59 - hostName = "jubilife"; 60 - }; 61 - 62 - services.udev.extraRules = let 63 - mkRule = as: lib.concatStringsSep ", " as; 64 - mkRules = rs: lib.concatStringsSep "\n" rs; 65 - in 66 - mkRules [ 67 - (mkRule [ 68 - ''ACTION=="add|change"'' 69 - ''SUBSYSTEM=="block"'' 70 - ''KERNEL=="sd[a-z]"'' 71 - ''ATTR{queue/rotational}=="1"'' 72 - ''RUN+="${pkgs.hdparm}/bin/hdparm -B 90 -S 41 /dev/%k"'' 73 - ]) 74 - ]; 75 - 76 - system.stateVersion = "25.11"; 77 - myDisko.installDrive = "/dev/disk/by-id/nvme-PNY_CS2130_1TB_SSD_PNY211821050701050CC"; 78 - 79 - myHardware = { 80 - amd.cpu.enable = true; 81 - intel.gpu.enable = true; 82 - profiles.base.enable = true; 83 - }; 84 - 85 - myNixOS = { 86 - base.enable = true; 87 - 88 - profiles = { 89 - arr = { 90 - enable = true; 91 - dataDir = "/mnt/Data"; 92 - }; 93 - 94 - autoUpgrade = { 95 - enable = true; 96 - dates = "04:15"; 97 - }; 98 - 99 - backups = { 100 - enable = true; 101 - extraJobs.dizquetv = { 102 - paths = ["/mnt/Data/dizquetv"]; 103 - repository = "rclone:b2:aly-backups/${config.networking.hostName}/dizquetv"; 104 - }; 105 - }; 106 - btrfs.enable = true; 107 - 108 - b2-mounts = { 109 - enable = true; 110 - cacheDir = "/mnt/Data/.rclone-cache"; 111 - audioCacheSize = "50G"; 112 - audioReadAhead = "3G"; 113 - videoCacheSize = "300G"; 114 - videoReadAhead = "5G"; 115 - }; 116 - 117 - vps.enable = true; 118 - swap.enable = true; 119 - zram.enable = true; 120 - 121 - k3s = { 122 - enable = true; 123 - role = "agent"; 124 - serverAddr = "https://pastoria:6443"; 125 - zone = "home"; 126 - }; 127 - }; 128 - 129 - programs = { 130 - lanzaboote.enable = true; 131 - nix.enable = true; 132 - podman.enable = true; 133 - }; 134 - 135 - services = { 136 - caddy.enable = true; 137 - 138 - forgejo-runner = { 139 - enable = true; 140 - dockerContainers = 3; 141 - nativeRunners = 2; 142 - }; 143 - 144 - plex = { 145 - enable = true; 146 - dataDir = "/mnt/Data"; 147 - tautulli.enable = true; 148 - }; 149 - 150 - prometheusNode.enable = true; 151 - alloy.enable = true; 152 - 153 - qbittorrent = { 154 - port = 8080; 155 - enable = true; 156 - }; 157 - 158 - syncthing = { 159 - enable = true; 160 - certFile = config.sops.secrets.syncthingCert.path; 161 - keyFile = config.sops.secrets.syncthingKey.path; 162 - romsPath = "${dataDirectory}/syncthing/ROMs"; 163 - syncROMs = true; 164 - user = "aly"; 165 - }; 166 - 167 - tailscale = { 168 - enable = true; 169 - caddy.enable = true; 170 - }; 171 - }; 172 - }; 173 - 174 - sops.secrets = { 175 - syncthingCert = { 176 - sopsFile = "${self}/secrets/syncthing.yaml"; 177 - key = "jubilife_cert"; 178 - }; 179 - syncthingKey = { 180 - sopsFile = "${self}/secrets/syncthing.yaml"; 181 - key = "jubilife_key"; 182 - }; 183 - }; 184 - 185 - myUsers.aly = { 186 - enable = true; 187 - password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 188 - }; 189 - }
-43
nix/hosts/jubilife/oci.nix
··· 1 - _: { 2 - systemd.tmpfiles.rules = [ 3 - "d /mnt/Data/dizquetv 0755 root root" 4 - "d /mnt/Data/arm/home 0755 1000 1000 - -" 5 - "d /mnt/Data/arm/config 0755 1000 1000 - -" 6 - "d /mnt/Data/arm 0755 1000 1000 - -" 7 - ]; 8 - 9 - virtualisation.oci-containers.containers = { 10 - # arm = { 11 - # autoStart = true; 12 - # image = "automaticrippingmachine/automatic-ripping-machine:latest"; 13 - # ports = ["8181:8080"]; 14 - 15 - # volumes = [ 16 - # "/mnt/Data/arm/home:/home/arm" 17 - # "/mnt/Data/arm/config:/etc/arm/config" 18 - # ]; 19 - 20 - # extraOptions = [ 21 - # # Needed for ARM to work correctly - by default `CAP_SYS_ADMIN` is dropped 22 - # # which blocks `mount()` calls within the container 23 - # # This is needed in order to `mount /dev/sr0 /mnt/dev/sr0` for ripping, which may be avoidable by 24 - # # handling mounts outside of the container, and having `/mnt/dev` bind mounted into the container. 25 - # "--privileged" 26 - # # Pass the CD/Bluray/DVD drive to the container 27 - # "--device=/dev/sr0:/dev/sr0" 28 - # "--pull=always" 29 - # ]; 30 - # }; 31 - 32 - dizquetv = { 33 - image = "vexorian/dizquetv:latest"; 34 - extraOptions = ["--pull=always"]; 35 - ports = ["0.0.0.0:8000:8000"]; 36 - 37 - volumes = [ 38 - "/mnt/Data/dizquetv:/home/node/app/.dizquetv" 39 - "/etc/localtime:/etc/localtime:ro" 40 - ]; 41 - }; 42 - }; 43 - }
-67
nix/hosts/jubilife/prometheus.nix
··· 1 - { 2 - config, 3 - self, 4 - ... 5 - }: { 6 - sops.secrets = { 7 - bazarrApiKey = { 8 - sopsFile = "${self}/secrets/arr.yaml"; 9 - key = "bazarr_api_key"; 10 - }; 11 - lidarrApiKey = { 12 - sopsFile = "${self}/secrets/arr.yaml"; 13 - key = "lidarr_api_key"; 14 - }; 15 - prowlarrApiKey = { 16 - sopsFile = "${self}/secrets/arr.yaml"; 17 - key = "prowlarr_api_key"; 18 - }; 19 - radarrApiKey = { 20 - sopsFile = "${self}/secrets/arr.yaml"; 21 - key = "radarr_api_key"; 22 - }; 23 - sonarrApiKey = { 24 - sopsFile = "${self}/secrets/arr.yaml"; 25 - key = "sonarr_api_key"; 26 - }; 27 - }; 28 - 29 - services.prometheus.exporters = { 30 - exportarr-bazarr = { 31 - enable = true; 32 - apiKeyFile = config.sops.secrets.bazarrApiKey.path; 33 - port = 9708; 34 - url = "https://bazarr.narwhal-snapper.ts.net"; 35 - }; 36 - 37 - exportarr-lidarr = { 38 - enable = true; 39 - apiKeyFile = config.sops.secrets.lidarrApiKey.path; 40 - port = 9709; 41 - url = "https://lidarr.narwhal-snapper.ts.net"; 42 - }; 43 - 44 - exportarr-prowlarr = { 45 - enable = true; 46 - apiKeyFile = config.sops.secrets.prowlarrApiKey.path; 47 - port = 9710; 48 - url = "https://prowlarr.narwhal-snapper.ts.net"; 49 - }; 50 - 51 - exportarr-radarr = { 52 - enable = true; 53 - apiKeyFile = config.sops.secrets.radarrApiKey.path; 54 - port = 9711; 55 - url = "https://radarr.narwhal-snapper.ts.net"; 56 - }; 57 - 58 - exportarr-sonarr = { 59 - enable = true; 60 - apiKeyFile = config.sops.secrets.sonarrApiKey.path; 61 - port = 9712; 62 - url = "https://sonarr.narwhal-snapper.ts.net"; 63 - }; 64 - 65 - smartctl.enable = true; 66 - }; 67 - }
-224
nix/hosts/jubilife/services.nix
··· 1 - { 2 - config, 3 - self, 4 - ... 5 - }: let 6 - dataDirectory = "/mnt/Data"; 7 - tnet = "narwhal-snapper.ts.net"; 8 - in { 9 - sops = { 10 - templates."immich-config.json" = { 11 - owner = "immich"; 12 - content = '' 13 - { 14 - "oauth": { 15 - "enabled": true, 16 - "issuerUrl": "https://id.cute.haus", 17 - "clientId": "${config.sops.placeholder.immichOauthClientId}", 18 - "clientSecret": "${config.sops.placeholder.immichOauthClientSecret}", 19 - "scope": "openid email profile", 20 - "buttonText": "Sign in with cute.haus", 21 - "autoRegister": true, 22 - "autoLaunch": false, 23 - "mobileOverrideEnabled": false, 24 - "mobileRedirectUri": "" 25 - } 26 - } 27 - ''; 28 - }; 29 - 30 - secrets = { 31 - immichOauthClientId = { 32 - sopsFile = "${self}/secrets/immich.yaml"; 33 - key = "oauth/client_id"; 34 - owner = "immich"; 35 - }; 36 - immichOauthClientSecret = { 37 - sopsFile = "${self}/secrets/immich.yaml"; 38 - key = "oauth/client_secret"; 39 - owner = "immich"; 40 - }; 41 - photoprismAdminPass = { 42 - sopsFile = "${self}/secrets/photoprism.yaml"; 43 - key = "ADMIN_PASSWORD"; 44 - }; 45 - }; 46 - }; 47 - 48 - networking.firewall.allowedTCPPorts = [6881]; 49 - 50 - services = { 51 - caddy.virtualHosts = { 52 - "bazarr.${tnet}".extraConfig = '' 53 - bind tailscale/bazarr 54 - encode zstd gzip 55 - reverse_proxy jubilife:6767 56 - ''; 57 - 58 - "jellyfin.${tnet}".extraConfig = '' 59 - bind tailscale/jellyfin 60 - encode zstd gzip 61 - reverse_proxy jubilife:8096 { 62 - flush_interval -1 63 - } 64 - ''; 65 - 66 - "lidarr.${tnet}".extraConfig = '' 67 - bind tailscale/lidarr 68 - encode zstd gzip 69 - reverse_proxy jubilife:8686 70 - ''; 71 - 72 - "navidrome.${tnet}".extraConfig = '' 73 - bind tailscale/navidrome 74 - encode zstd gzip 75 - reverse_proxy snowpoint:4533 76 - ''; 77 - 78 - "ollama.${tnet}".extraConfig = '' 79 - bind tailscale/ollama 80 - encode zstd gzip 81 - reverse_proxy jubilife:11434 82 - ''; 83 - 84 - "prowlarr.${tnet}".extraConfig = '' 85 - bind tailscale/prowlarr 86 - encode zstd gzip 87 - reverse_proxy jubilife:9696 88 - ''; 89 - 90 - "qbittorrent.${tnet}".extraConfig = '' 91 - bind tailscale/qbittorrent 92 - encode zstd gzip 93 - reverse_proxy jubilife:8080 94 - ''; 95 - 96 - "radarr.${tnet}".extraConfig = '' 97 - bind tailscale/radarr 98 - encode zstd gzip 99 - reverse_proxy jubilife:7878 100 - ''; 101 - 102 - "sonarr.${tnet}".extraConfig = '' 103 - bind tailscale/sonarr 104 - encode zstd gzip 105 - reverse_proxy jubilife:8989 106 - ''; 107 - 108 - "tautulli.${tnet}".extraConfig = '' 109 - bind tailscale/tautulli 110 - encode zstd gzip 111 - reverse_proxy jubilife:8181 112 - ''; 113 - }; 114 - 115 - immich = { 116 - enable = true; 117 - host = "0.0.0.0"; 118 - mediaLocation = "${dataDirectory}/immich"; 119 - openFirewall = true; 120 - port = 2283; 121 - environment.IMMICH_CONFIG_FILE = config.sops.templates."immich-config.json".path; 122 - }; 123 - 124 - jellyfin = { 125 - enable = true; 126 - openFirewall = true; 127 - dataDir = "${dataDirectory}/jellyfin"; 128 - }; 129 - 130 - nfs.server = { 131 - enable = true; 132 - exports = '' 133 - /mnt/Data 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=0) 134 - /mnt/Media 100.64.0.0/10(rw,sync,no_subtree_check,no_root_squash,fsid=1) 135 - ''; 136 - }; 137 - 138 - ollama = { 139 - enable = true; 140 - host = "0.0.0.0"; 141 - 142 - loadModels = [ 143 - "gemma3:12b" 144 - "gemma3:4b" 145 - "nomic-embed-text" 146 - ]; 147 - 148 - openFirewall = true; 149 - }; 150 - 151 - samba = { 152 - enable = true; 153 - openFirewall = true; 154 - 155 - settings = { 156 - global = { 157 - security = "user"; 158 - "map to guest" = "Bad User"; 159 - 160 - # Protocol tuning 161 - "server min protocol" = "SMB3"; 162 - "server max protocol" = "SMB3_11"; 163 - 164 - # Performance options 165 - "socket options" = "TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=262144 SO_SNDBUF=262144"; 166 - "use sendfile" = "no"; # Plex compatibility 167 - "aio read size" = "1"; 168 - "aio write size" = "1"; 169 - "min receivefile size" = "131072"; # Bump slightly from 16K to 128K 170 - "max xmit" = "65535"; # Samba's max recommended for best throughput 171 - 172 - # Locking & latency 173 - "strict locking" = "no"; 174 - "oplocks" = "yes"; 175 - "level2 oplocks" = "yes"; 176 - }; 177 - 178 - Data = { 179 - "create mask" = "0755"; 180 - "directory mask" = "0755"; 181 - "force group" = "users"; 182 - "force user" = "aly"; 183 - "guest ok" = "yes"; 184 - "read only" = "no"; 185 - browseable = "yes"; 186 - comment = "Data @ ${config.networking.hostName}"; 187 - path = dataDirectory; 188 - }; 189 - 190 - Media = { 191 - "create mask" = "0755"; 192 - "directory mask" = "0755"; 193 - "force group" = "users"; 194 - "force user" = "aly"; 195 - "guest ok" = "yes"; 196 - "read only" = "no"; 197 - browseable = "yes"; 198 - comment = "Media @ ${config.networking.hostName}"; 199 - path = "/mnt/Media"; 200 - }; 201 - }; 202 - }; 203 - 204 - samba-wsdd = { 205 - enable = true; 206 - openFirewall = true; 207 - }; 208 - 209 - smartd.enable = true; 210 - 211 - snapper.configs.media = { 212 - ALLOW_GROUPS = ["users"]; 213 - FSTYPE = "btrfs"; 214 - SUBVOLUME = "/mnt/Media"; 215 - TIMELINE_CLEANUP = true; 216 - TIMELINE_CREATE = true; 217 - }; 218 - 219 - tuned = { 220 - enable = true; 221 - settings.dynamic_tuning = true; 222 - }; 223 - }; 224 - }
+82
nix/hosts/pastoria.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.pastoria = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + base 10 + fail2ban 11 + flake-url 12 + known-hosts 13 + locale-en-us 14 + njust 15 + recipes 16 + performance 17 + ssh-keys 18 + users 19 + alloy 20 + auto-upgrade 21 + backups 22 + k3s-node 23 + nix-config 24 + podman 25 + prometheus-node 26 + swap 27 + tailscale 28 + vps 29 + zram 30 + 31 + inputs.disko.nixosModules.disko 32 + inputs.sops-nix.nixosModules.sops 33 + config.flake.diskoConfigurations.lvm-ext4 34 + ( 35 + {modulesPath, ...}: { 36 + imports = [ 37 + "${modulesPath}/profiles/qemu-guest.nix" 38 + ]; 39 + 40 + boot = { 41 + initrd.availableKernelModules = [ 42 + "ahci" 43 + "sd_mod" 44 + "sr_mod" 45 + "virtio_pci" 46 + "virtio_scsi" 47 + "xhci_pci" 48 + ]; 49 + 50 + loader.grub = { 51 + efiSupport = true; 52 + efiInstallAsRemovable = true; 53 + }; 54 + }; 55 + 56 + networking.hostName = "pastoria"; 57 + nixpkgs.hostPlatform = "x86_64-linux"; 58 + system.stateVersion = "26.05"; 59 + myDisko.installDrive = "/dev/sda"; 60 + 61 + myAutoUpgrade.dates = "01:45"; 62 + 63 + myK3s = { 64 + role = "server"; 65 + serverAddr = "https://solaceon:6443"; 66 + zone = "cloud-ovhcloud"; 67 + ingress = true; 68 + }; 69 + 70 + mySwap.size = 4096; 71 + } 72 + ) 73 + 74 + { 75 + nixpkgs = { 76 + overlays = [self.overlays.default]; 77 + config.allowUnfree = true; 78 + }; 79 + } 80 + ]; 81 + }; 82 + }
-7
nix/hosts/pastoria/README.md
··· 1 - # 🦇 Pastoria 2 - 3 - ## Overview 4 - 5 - VPS running on OVH Cloud. 6 - 7 - ---
-74
nix/hosts/pastoria/default.nix
··· 1 - { 2 - modulesPath, 3 - self, 4 - ... 5 - }: { 6 - imports = [ 7 - "${modulesPath}/profiles/qemu-guest.nix" 8 - self.diskoConfigurations.lvm-ext4 9 - ]; 10 - 11 - boot = { 12 - initrd.availableKernelModules = [ 13 - "ahci" 14 - "sd_mod" 15 - "sr_mod" 16 - "virtio_pci" 17 - "virtio_scsi" 18 - "xhci_pci" 19 - ]; 20 - 21 - loader.grub = { 22 - efiSupport = true; 23 - efiInstallAsRemovable = true; 24 - }; 25 - }; 26 - 27 - networking.hostName = "pastoria"; 28 - nixpkgs.hostPlatform = "x86_64-linux"; 29 - system.stateVersion = "26.05"; 30 - myDisko.installDrive = "/dev/sda"; 31 - 32 - myNixOS = { 33 - base.enable = true; 34 - 35 - profiles = { 36 - autoUpgrade = { 37 - enable = true; 38 - dates = "01:45"; 39 - }; 40 - 41 - backups.enable = true; 42 - 43 - k3s = { 44 - enable = true; 45 - role = "server"; 46 - serverAddr = "https://solaceon:6443"; 47 - zone = "cloud-ovhcloud"; 48 - ingress = true; 49 - }; 50 - 51 - vps.enable = true; 52 - 53 - swap = { 54 - enable = true; 55 - size = 4096; 56 - }; 57 - 58 - zram.enable = true; 59 - }; 60 - 61 - programs = { 62 - nix.enable = true; 63 - podman.enable = true; 64 - }; 65 - 66 - services = { 67 - prometheusNode.enable = true; 68 - alloy.enable = true; 69 - tailscale.enable = true; 70 - }; 71 - }; 72 - 73 - myUsers.root.enable = true; 74 - }
+155
nix/hosts/snowpoint.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.snowpoint = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + base 10 + fail2ban 11 + flake-url 12 + known-hosts 13 + locale-en-us 14 + njust 15 + recipes 16 + performance 17 + ssh-keys 18 + users 19 + alloy 20 + auto-upgrade 21 + b2-mounts 22 + backups 23 + cachefilesd 24 + data-share 25 + k3s-node 26 + media-share 27 + nix-config 28 + plex 29 + prometheus-node 30 + swap 31 + syncthing 32 + tailscale 33 + vps 34 + 35 + inputs.disko.nixosModules.disko 36 + inputs.sops-nix.nixosModules.sops 37 + config.flake.diskoConfigurations.lvm-ext4 38 + ( 39 + {config, ...}: { 40 + boot = { 41 + initrd = { 42 + availableKernelModules = [ 43 + "virtio_net" 44 + "virtio_pci" 45 + "virtio_mmio" 46 + "virtio_blk" 47 + "virtio_scsi" 48 + "9p" 49 + "9pnet_virtio" 50 + ]; 51 + 52 + kernelModules = [ 53 + "virtio_balloon" 54 + "virtio_console" 55 + "virtio_rng" 56 + "virtio_gpu" 57 + ]; 58 + }; 59 + 60 + loader.grub = { 61 + efiSupport = true; 62 + efiInstallAsRemovable = true; 63 + }; 64 + }; 65 + 66 + fileSystems = {}; 67 + 68 + networking.hostName = "snowpoint"; 69 + nixpkgs.hostPlatform = "x86_64-linux"; 70 + system.stateVersion = "25.11"; 71 + 72 + sops.secrets = { 73 + navidrome = { 74 + sopsFile = ../../secrets/navidrome.yaml; 75 + key = "env"; 76 + }; 77 + syncthingCert = { 78 + sopsFile = ../../secrets/syncthing.yaml; 79 + key = "snowpoint_cert"; 80 + }; 81 + syncthingKey = { 82 + sopsFile = ../../secrets/syncthing.yaml; 83 + key = "snowpoint_key"; 84 + }; 85 + }; 86 + 87 + services = { 88 + qemuGuest.enable = true; 89 + 90 + navidrome = { 91 + enable = true; 92 + environmentFile = config.sops.secrets.navidrome.path; 93 + 94 + settings = { 95 + Address = "0.0.0.0"; 96 + DefaultTheme = "Auto"; 97 + EnableUserEditing = false; 98 + MusicFolder = "/mnt/Media/Music"; 99 + Port = 4533; 100 + SubsonicArtistParticipations = true; 101 + UIWelcomeMessage = "Welcome to Navidrome @ ${config.networking.hostName}"; 102 + 103 + # SSO via traefik-forward-auth in the cluster (auth.cute.haus). 104 + # TrustedSources is checked against the immediate TCP source — the 105 + # traefik pod inside the k3s pod CIDR — not against X-Forwarded-For. 106 + # Subsonic mobile clients hit caddy directly from the tailnet and 107 + # don't send the header; they fall back to local username/password. 108 + ExtAuth = { 109 + UserHeader = "X-Forwarded-User"; 110 + TrustedSources = builtins.concatStringsSep "," [ 111 + "100.64.0.0/10" 112 + "10.42.0.0/16" 113 + ]; 114 + }; 115 + }; 116 + }; 117 + }; 118 + 119 + systemd.services.navidrome.serviceConfig = { 120 + Restart = "on-failure"; 121 + RestartSec = "30s"; 122 + }; 123 + myDisko.installDrive = "/dev/vda"; 124 + 125 + myAutoUpgrade.dates = "03:30"; 126 + 127 + myB2Mounts.cacheDir = "/mnt/Backblaze/.rclone-cache"; 128 + 129 + myK3s = { 130 + role = "agent"; 131 + serverAddr = "https://pastoria:6443"; 132 + zone = "cloud-netcup"; 133 + ingress = true; 134 + }; 135 + 136 + mySyncthing = { 137 + certFile = config.sops.secrets.syncthingCert.path; 138 + keyFile = config.sops.secrets.syncthingKey.path; 139 + syncROMs = false; 140 + user = "aly"; 141 + }; 142 + 143 + myUsers.aly.password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 144 + } 145 + ) 146 + 147 + { 148 + nixpkgs = { 149 + overlays = [self.overlays.default]; 150 + config.allowUnfree = true; 151 + }; 152 + } 153 + ]; 154 + }; 155 + }
-7
nix/hosts/snowpoint/README.md
··· 1 - # Snowpoint 2 - 3 - ## Overview 4 - 5 - VPS running on Netcup. 6 - 7 - ---
-149
nix/hosts/snowpoint/default.nix
··· 1 - { 2 - config, 3 - self, 4 - ... 5 - }: { 6 - imports = [ 7 - self.diskoConfigurations.lvm-ext4 8 - ]; 9 - 10 - boot = { 11 - initrd = { 12 - availableKernelModules = [ 13 - "virtio_net" 14 - "virtio_pci" 15 - "virtio_mmio" 16 - "virtio_blk" 17 - "virtio_scsi" 18 - "9p" 19 - "9pnet_virtio" 20 - ]; 21 - 22 - kernelModules = [ 23 - "virtio_balloon" 24 - "virtio_console" 25 - "virtio_rng" 26 - "virtio_gpu" 27 - ]; 28 - }; 29 - 30 - loader.grub = { 31 - efiSupport = true; 32 - efiInstallAsRemovable = true; 33 - }; 34 - }; 35 - 36 - fileSystems = {}; 37 - 38 - networking.hostName = "snowpoint"; 39 - nixpkgs.hostPlatform = "x86_64-linux"; 40 - system.stateVersion = "25.11"; 41 - 42 - sops.secrets = { 43 - navidrome = { 44 - sopsFile = "${self}/secrets/navidrome.yaml"; 45 - key = "env"; 46 - }; 47 - syncthingCert = { 48 - sopsFile = "${self}/secrets/syncthing.yaml"; 49 - key = "snowpoint_cert"; 50 - }; 51 - syncthingKey = { 52 - sopsFile = "${self}/secrets/syncthing.yaml"; 53 - key = "snowpoint_key"; 54 - }; 55 - }; 56 - 57 - services = { 58 - qemuGuest.enable = true; 59 - 60 - navidrome = { 61 - enable = true; 62 - environmentFile = config.sops.secrets.navidrome.path; 63 - 64 - settings = { 65 - Address = "0.0.0.0"; 66 - DefaultTheme = "Auto"; 67 - EnableUserEditing = false; 68 - MusicFolder = "/mnt/Media/Music"; 69 - Port = 4533; 70 - SubsonicArtistParticipations = true; 71 - UIWelcomeMessage = "Welcome to Navidrome @ ${config.networking.hostName}"; 72 - 73 - # SSO via traefik-forward-auth in the cluster (auth.cute.haus). 74 - # TrustedSources is checked against the immediate TCP source — the 75 - # traefik pod inside the k3s pod CIDR — not against X-Forwarded-For. 76 - # Subsonic mobile clients hit caddy directly from the tailnet and 77 - # don't send the header; they fall back to local username/password. 78 - ExtAuth = { 79 - UserHeader = "X-Forwarded-User"; 80 - TrustedSources = builtins.concatStringsSep "," [ 81 - "100.64.0.0/10" # tailnet 82 - "10.42.0.0/16" # k3s pod CIDR (traefik) 83 - ]; 84 - }; 85 - }; 86 - }; 87 - }; 88 - 89 - # Make navidrome wait for /mnt/Media + restart if necessary. 90 - systemd.services.navidrome.serviceConfig = { 91 - Restart = "on-failure"; 92 - RestartSec = "30s"; 93 - }; 94 - myDisko.installDrive = "/dev/vda"; 95 - 96 - myNixOS = { 97 - base.enable = true; 98 - 99 - profiles = { 100 - autoUpgrade = { 101 - enable = true; 102 - dates = "03:30"; 103 - }; 104 - 105 - backups.enable = true; 106 - 107 - b2-mounts = { 108 - enable = true; 109 - cacheDir = "/mnt/Backblaze/.rclone-cache"; 110 - }; 111 - 112 - data-share.enable = true; 113 - media-share.enable = true; 114 - vps.enable = true; 115 - swap.enable = true; 116 - 117 - k3s = { 118 - enable = true; 119 - role = "agent"; 120 - serverAddr = "https://pastoria:6443"; 121 - zone = "cloud-netcup"; 122 - ingress = true; 123 - }; 124 - }; 125 - 126 - programs.nix.enable = true; 127 - 128 - services = { 129 - plex.enable = true; 130 - prometheusNode.enable = true; 131 - alloy.enable = true; 132 - 133 - syncthing = { 134 - enable = true; 135 - certFile = config.sops.secrets.syncthingCert.path; 136 - keyFile = config.sops.secrets.syncthingKey.path; 137 - syncROMs = false; 138 - user = "aly"; 139 - }; 140 - 141 - tailscale.enable = true; 142 - }; 143 - }; 144 - 145 - myUsers.aly = { 146 - enable = true; 147 - password = "$6$JTk2qi27OpA2fOAY$ZgTDg0wbmbwHUD..0xT4xYX.AR5hWQFCMVmn8G88yi3IAY7015AupovTpfy0arkI7nl/IDu5L09bzLKeXGvJC1"; 148 - }; 149 - }
+102
nix/hosts/solaceon.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.solaceon = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + base 10 + fail2ban 11 + flake-url 12 + known-hosts 13 + locale-en-us 14 + njust 15 + recipes 16 + performance 17 + ssh-keys 18 + users 19 + alloy 20 + auto-upgrade 21 + backups 22 + k3s-node 23 + nix-config 24 + podman 25 + prometheus-node 26 + swap 27 + tailscale 28 + vps 29 + zram 30 + 31 + inputs.disko.nixosModules.disko 32 + inputs.sops-nix.nixosModules.sops 33 + config.flake.diskoConfigurations.lvm-ext4 34 + ( 35 + { 36 + modulesPath, 37 + pkgs, 38 + ... 39 + }: { 40 + imports = [ 41 + "${modulesPath}/profiles/qemu-guest.nix" 42 + ]; 43 + 44 + boot = { 45 + initrd.availableKernelModules = [ 46 + "ahci" 47 + "sd_mod" 48 + "sr_mod" 49 + "virtio_pci" 50 + "virtio_scsi" 51 + "xhci_pci" 52 + ]; 53 + 54 + loader.grub = { 55 + efiSupport = true; 56 + efiInstallAsRemovable = true; 57 + }; 58 + }; 59 + 60 + networking = { 61 + firewall.allowedTCPPorts = [23 8282 8383]; 62 + hostName = "solaceon"; 63 + }; 64 + 65 + systemd.services.atbbs-telnet = { 66 + description = "TCP proxy for atbbs telnet"; 67 + wantedBy = ["multi-user.target"]; 68 + after = ["network.target"]; 69 + 70 + serviceConfig = { 71 + ExecStart = "${pkgs.socat}/bin/socat TCP-LISTEN:23,fork,reuseaddr TCP:eterna:2323"; 72 + Restart = "always"; 73 + }; 74 + }; 75 + 76 + nixpkgs.hostPlatform = "x86_64-linux"; 77 + system.stateVersion = "25.11"; 78 + time.timeZone = "America/New_York"; 79 + myDisko.installDrive = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_62292463"; 80 + 81 + myAutoUpgrade.dates = "02:00"; 82 + 83 + myK3s = { 84 + role = "server"; 85 + clusterInit = true; 86 + zone = "cloud-hetzner"; 87 + ingress = true; 88 + }; 89 + 90 + mySwap.size = 2048; 91 + } 92 + ) 93 + 94 + { 95 + nixpkgs = { 96 + overlays = [self.overlays.default]; 97 + config.allowUnfree = true; 98 + }; 99 + } 100 + ]; 101 + }; 102 + }
-7
nix/hosts/solaceon/README.md
··· 1 - # 🦭 Solaceon 2 - 3 - ## Overview 4 - 5 - VPS running on Hetzner Cloud. 6 - 7 - ---
-91
nix/hosts/solaceon/default.nix
··· 1 - { 2 - modulesPath, 3 - pkgs, 4 - self, 5 - ... 6 - }: { 7 - imports = [ 8 - "${modulesPath}/profiles/qemu-guest.nix" 9 - self.diskoConfigurations.lvm-ext4 10 - ]; 11 - 12 - boot = { 13 - initrd.availableKernelModules = [ 14 - "ahci" 15 - "sd_mod" 16 - "sr_mod" 17 - "virtio_pci" 18 - "virtio_scsi" 19 - "xhci_pci" 20 - ]; 21 - 22 - loader.grub = { 23 - efiSupport = true; 24 - efiInstallAsRemovable = true; 25 - }; 26 - }; 27 - 28 - networking = { 29 - firewall.allowedTCPPorts = [23 8282 8383]; 30 - hostName = "solaceon"; 31 - }; 32 - 33 - systemd.services.atbbs-telnet = { 34 - description = "TCP proxy for atbbs telnet"; 35 - wantedBy = ["multi-user.target"]; 36 - after = ["network.target"]; 37 - 38 - serviceConfig = { 39 - ExecStart = "${pkgs.socat}/bin/socat TCP-LISTEN:23,fork,reuseaddr TCP:eterna:2323"; 40 - Restart = "always"; 41 - }; 42 - }; 43 - 44 - nixpkgs.hostPlatform = "x86_64-linux"; 45 - system.stateVersion = "25.11"; 46 - time.timeZone = "America/New_York"; 47 - myDisko.installDrive = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_62292463"; 48 - 49 - myNixOS = { 50 - base.enable = true; 51 - 52 - profiles = { 53 - autoUpgrade = { 54 - enable = true; 55 - dates = "02:00"; 56 - }; 57 - 58 - backups.enable = true; 59 - 60 - k3s = { 61 - enable = true; 62 - role = "server"; 63 - clusterInit = true; 64 - zone = "cloud-hetzner"; 65 - ingress = true; 66 - }; 67 - 68 - vps.enable = true; 69 - 70 - swap = { 71 - enable = true; 72 - size = 2048; 73 - }; 74 - 75 - zram.enable = true; 76 - }; 77 - 78 - programs = { 79 - nix.enable = true; 80 - podman.enable = true; 81 - }; 82 - 83 - services = { 84 - prometheusNode.enable = true; 85 - alloy.enable = true; 86 - tailscale.enable = true; 87 - }; 88 - }; 89 - 90 - myUsers.root.enable = true; 91 - }
+39
nix/hosts/twinleaf.nix
··· 1 + { 2 + config, 3 + inputs, 4 + self, 5 + ... 6 + }: { 7 + flake.nixosConfigurations.twinleaf = inputs.nixpkgs.lib.nixosSystem { 8 + modules = with config.flake.modules.nixos; [ 9 + iso 10 + nix-config 11 + njust 12 + recipes 13 + 14 + inputs.sops-nix.nixosModules.sops 15 + ( 16 + { 17 + lib, 18 + modulesPath, 19 + ... 20 + }: { 21 + imports = [ 22 + "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" 23 + ]; 24 + 25 + image.baseName = lib.mkForce "twinleaf"; 26 + networking.hostName = "twinleaf"; 27 + nixpkgs.hostPlatform = "x86_64-linux"; 28 + } 29 + ) 30 + 31 + { 32 + nixpkgs = { 33 + overlays = [self.overlays.default]; 34 + config.allowUnfree = true; 35 + }; 36 + } 37 + ]; 38 + }; 39 + }
-7
nix/hosts/twinleaf/README.md
··· 1 - # 🌱 Twinleaf 2 - 3 - ## Overview 4 - 5 - Minimal NixOS installation ISO for basic system installation and recovery. 6 - 7 - ---
-20
nix/hosts/twinleaf/default.nix
··· 1 - { 2 - lib, 3 - modulesPath, 4 - ... 5 - }: { 6 - imports = [ 7 - "${modulesPath}/installer/cd-dvd/installation-cd-minimal.nix" 8 - ]; 9 - 10 - image.baseName = lib.mkForce "twinleaf"; 11 - networking.hostName = "twinleaf"; 12 - nixpkgs.hostPlatform = "x86_64-linux"; 13 - 14 - myNixOS = { 15 - profiles.iso.enable = true; 16 - programs.nix.enable = true; 17 - }; 18 - 19 - myUsers.root.enable = true; 20 - }
nix/modules/flake/devShells.nix nix/devShells.nix
nix/modules/flake/files/zed.nix nix/files/zed.nix
-52
nix/modules/flake/nixos/default.nix
··· 1 - { 2 - config, 3 - self, 4 - inputs, 5 - ... 6 - }: { 7 - flake = { 8 - modules.nixos = { 9 - hardware = inputs.import-tree ../../hardware; 10 - nixos = inputs.import-tree ../../nixos; 11 - }; 12 - 13 - nixosConfigurations = let 14 - modules = config.flake.modules.nixos; 15 - in 16 - inputs.nixpkgs.lib.genAttrs [ 17 - "eterna" 18 - "jubilife" 19 - "pastoria" 20 - "snowpoint" 21 - "solaceon" 22 - "twinleaf" 23 - ] ( 24 - host: 25 - inputs.nixpkgs.lib.nixosSystem { 26 - modules = [ 27 - (inputs.import-tree ../../../hosts/${host}) 28 - modules.locale-en-us 29 - modules.sshKeys 30 - inputs.disko.nixosModules.disko 31 - inputs.lanzaboote.nixosModules.lanzaboote 32 - inputs.sops-nix.nixosModules.sops 33 - modules.hardware 34 - modules.nixos 35 - modules.users 36 - 37 - { 38 - nixpkgs = { 39 - overlays = [ 40 - self.overlays.default 41 - ]; 42 - 43 - config.allowUnfree = true; 44 - }; 45 - } 46 - ]; 47 - 48 - specialArgs = {inherit self;}; 49 - } 50 - ); 51 - }; 52 - }
nix/modules/flake/nixos/disko.nix nix/disko.nix
nix/modules/flake/nixos/locale-en-us.nix nix/nixos/locale-en-us.nix
-18
nix/modules/flake/nixos/ssh-keys.nix
··· 1 - _: { 2 - flake.modules.nixos.sshKeys = { 3 - lib, 4 - self, 5 - ... 6 - }: let 7 - keyFiles = builtins.attrNames (builtins.readDir "${self}/keys"); 8 - in { 9 - options.myNixOS.sshKeyFiles = lib.mkOption { 10 - type = lib.types.attrsOf (lib.types.listOf lib.types.path); 11 - description = "Cached SSH key file paths, read once to avoid repeated filesystem scans."; 12 - default = { 13 - aly = lib.map (file: "${self}/keys/${file}") (lib.filter (file: lib.hasPrefix "aly_" file) keyFiles); 14 - root = lib.map (file: "${self}/keys/${file}") (lib.filter (file: lib.hasPrefix "root_" file) keyFiles); 15 - }; 16 - }; 17 - }; 18 - }
-66
nix/modules/flake/nixos/users.nix
··· 1 - _: { 2 - flake.modules.nixos.users = { 3 - config, 4 - lib, 5 - pkgs, 6 - ... 7 - }: let 8 - mkUser = user: { 9 - enable = lib.mkEnableOption "${user}."; 10 - 11 - password = lib.mkOption { 12 - default = null; 13 - description = "Hashed password for ${user}."; 14 - type = lib.types.nullOr lib.types.str; 15 - }; 16 - }; 17 - in { 18 - options.myUsers = { 19 - defaultGroups = lib.mkOption { 20 - description = "Default groups for desktop users."; 21 - default = [ 22 - "cdrom" 23 - "dialout" 24 - "docker" 25 - "libvirtd" 26 - "lp" 27 - "networkmanager" 28 - "plugdev" 29 - "scanner" 30 - "transmission" 31 - "video" 32 - "wheel" 33 - ]; 34 - }; 35 - 36 - root.enable = lib.mkEnableOption "root user configuration." // {default = true;}; 37 - aly = mkUser "aly"; 38 - }; 39 - 40 - config = lib.mkMerge [ 41 - (lib.mkIf (config.myUsers.root.enable or config.myUsers.aly.enable or config.myUsers.dustin.enable) { 42 - programs.fish.enable = true; 43 - 44 - users = { 45 - defaultUserShell = pkgs.fish; 46 - mutableUsers = false; 47 - 48 - users.root.openssh.authorizedKeys.keyFiles = config.myNixOS.sshKeyFiles.aly; 49 - }; 50 - }) 51 - 52 - (lib.mkIf config.myUsers.aly.enable { 53 - users.users.aly = { 54 - description = "Aly Raffauf"; 55 - extraGroups = config.myUsers.defaultGroups; 56 - hashedPassword = config.myUsers.aly.password; 57 - isNormalUser = true; 58 - 59 - openssh.authorizedKeys.keyFiles = config.myNixOS.sshKeyFiles.aly; 60 - 61 - uid = 1000; 62 - }; 63 - }) 64 - ]; 65 - }; 66 - }
nix/modules/flake/nynx.nix nix/deployments.nix
nix/modules/flake/overlays.nix nix/overlays.nix
nix/modules/flake/packages.nix nix/packages.nix
nix/modules/flake/treefmt.nix nix/treefmt.nix
-22
nix/modules/hardware/amd/cpu.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.amd.cpu.enable = lib.mkEnableOption "AMD CPU configuration."; 7 - 8 - config = lib.mkIf config.myHardware.amd.cpu.enable { 9 - boot = { 10 - blacklistedKernelModules = ["k10temp"]; # Conflicts with zenpower 11 - extraModulePackages = with config.boot.kernelPackages; [zenpower]; 12 - 13 - kernelModules = [ 14 - "kvm-amd" 15 - "zenpower" # Improved temperature monitoring 16 - ]; 17 - }; 18 - 19 - hardware.cpu.amd.updateMicrocode = true; 20 - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 21 - }; 22 - }
-23
nix/modules/hardware/amd/gpu.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.amd.gpu.enable = lib.mkEnableOption "AMD GPU configuration."; 7 - 8 - config = lib.mkIf config.myHardware.amd.gpu.enable { 9 - environment.variables = { 10 - DPAU_DRIVER = "radeonsi"; 11 - GSK_RENDERER = "ngl"; 12 - }; 13 - 14 - hardware = { 15 - amdgpu = { 16 - initrd.enable = true; 17 - opencl.enable = true; 18 - }; 19 - 20 - graphics.enable = true; 21 - }; 22 - }; 23 - }
-24
nix/modules/hardware/beelink/mini/s12pro.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.beelink.mini.s12pro.enable = lib.mkEnableOption "Beelink Mini S12Pro hardware configuration."; 7 - 8 - config = lib.mkIf config.myHardware.beelink.mini.s12pro.enable { 9 - boot.initrd.availableKernelModules = [ 10 - "ahci" 11 - "sd_mod" 12 - "xhci_pci" 13 - ]; 14 - 15 - myHardware = { 16 - intel = { 17 - cpu.enable = true; 18 - gpu.enable = true; 19 - }; 20 - 21 - profiles.base.enable = true; 22 - }; 23 - }; 24 - }
-41
nix/modules/hardware/framework/13/intel-11th.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myHardware.framework.laptop13.intel-11th.enable = lib.mkEnableOption "Framework Laptop 13 Intel 11th gen hardware configuration."; 8 - 9 - config = lib.mkIf config.myHardware.framework.laptop13.intel-11th.enable { 10 - boot = { 11 - blacklistedKernelModules = ["cros-usbpd-charger"]; 12 - initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod"]; 13 - kernelPackages = lib.mkIf (lib.versionOlder pkgs.linux.version "6.11") (lib.mkDefault pkgs.linuxPackages_latest); 14 - 15 - kernelParams = [ 16 - "nvme.noacpi=1" # https://community.frame.work/t/linux-battery-life-tuning/6665/156 17 - ]; 18 - }; 19 - 20 - hardware.acpilight.enable = true; 21 - powerManagement.powertop.enable = lib.mkForce false; 22 - 23 - services.udev.extraRules = '' 24 - ## Framework 13 -- Fix headphone noise when on powersave 25 - ## https://community.frame.work/t/headphone-jack-intermittent-noise/5246/55 26 - SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0xa0e0", ATTR{power/control}="on" 27 - ''; 28 - 29 - myHardware = { 30 - intel = { 31 - cpu.enable = true; 32 - gpu.enable = true; 33 - }; 34 - 35 - profiles = { 36 - base.enable = true; 37 - laptop.enable = true; 38 - }; 39 - }; 40 - }; 41 - }
+7 -14
nix/modules/hardware/framework/13/laptop13.nix nix/hardware/framework/13/laptop13.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myHardware.framework.laptop13.enable = lib.mkOption { 8 - type = lib.types.bool; 9 - default = config.myHardware.framework.laptop13.intel-11th.enable; 10 - description = "Framework Laptop 13 specific hardware configuration"; 11 - }; 12 - 13 - config = lib.mkIf config.myHardware.framework.laptop13.enable { 1 + _: { 2 + flake.modules.nixos.framework-13 = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: { 14 8 boot = { 15 9 extraModulePackages = with config.boot.kernelPackages; [ 16 10 framework-laptop-kmod 17 11 ]; 18 12 19 13 kernelModules = [ 20 - # https://github.com/DHowett/framework-laptop-kmod?tab=readme-ov-file#usage 21 14 "cros_ec_lpcs" 22 15 "cros_ec" 23 16 ];
-13
nix/modules/hardware/intel/cpu.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.intel.cpu.enable = lib.mkEnableOption "Intel CPU configuration."; 7 - 8 - config = lib.mkIf config.myHardware.intel.cpu.enable { 9 - boot.kernelModules = ["kvm-intel"]; 10 - hardware.cpu.intel.updateMicrocode = true; 11 - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 12 - }; 13 - }
-55
nix/modules/hardware/intel/gpu.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myHardware.intel.gpu = { 8 - enable = lib.mkEnableOption "Intel GPU configuration."; 9 - 10 - driver = lib.mkOption { 11 - description = "Intel GPU driver to use"; 12 - 13 - type = lib.types.enum [ 14 - "i915" 15 - "xe" 16 - ]; 17 - 18 - default = "i915"; 19 - }; 20 - }; 21 - 22 - config = lib.mkIf config.myHardware.intel.gpu.enable { 23 - boot.initrd.kernelModules = [config.myHardware.intel.gpu.driver]; 24 - 25 - environment.sessionVariables = { 26 - LIBVA_DRIVER_NAME = "iHD"; 27 - VDPAU_DRIVER = "va_gl"; 28 - }; 29 - 30 - hardware = { 31 - intel-gpu-tools.enable = true; 32 - 33 - graphics = { 34 - enable = true; 35 - 36 - extraPackages = [ 37 - (pkgs.intel-vaapi-driver.override {enableHybridCodec = true;}) 38 - pkgs.intel-compute-runtime 39 - pkgs.intel-media-driver 40 - pkgs.intel-ocl 41 - pkgs.libvdpau-va-gl 42 - pkgs.vpl-gpu-rt 43 - ]; 44 - 45 - extraPackages32 = [ 46 - pkgs.driversi686Linux.intel-media-driver 47 - (pkgs.driversi686Linux.intel-vaapi-driver.override {enableHybridCodec = true;}) 48 - pkgs.driversi686Linux.libvdpau-va-gl 49 - ]; 50 - }; 51 - }; 52 - 53 - services.xserver.videoDrivers = ["modesetting"]; 54 - }; 55 - }
-26
nix/modules/hardware/lenovo/thinkcentre/m700.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.lenovo.thinkcentre.m700.enable = lib.mkEnableOption "Lenovo ThinkCentre M700 hardware configuration."; 7 - 8 - config = lib.mkIf config.myHardware.lenovo.thinkcentre.m700.enable { 9 - boot.initrd.availableKernelModules = [ 10 - "ahci" 11 - "sd_mod" 12 - "xhci_pci" 13 - ]; 14 - 15 - services.fwupd.enable = true; 16 - 17 - myHardware = { 18 - intel = { 19 - cpu.enable = true; 20 - gpu.enable = true; 21 - }; 22 - 23 - profiles.base.enable = true; 24 - }; 25 - }; 26 - }
+2 -8
nix/modules/hardware/nvidia/gpu.nix nix/hardware/nvidia/gpu.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.nvidia.gpu.enable = lib.mkEnableOption "NVIDIA GPU configuration."; 7 - 8 - config = lib.mkIf config.myHardware.nvidia.gpu.enable { 1 + _: { 2 + flake.modules.nixos.nvidia-gpu = {config, ...}: { 9 3 boot = { 10 4 extraModulePackages = [config.boot.kernelPackages.nvidia_x11]; 11 5 initrd.kernelModules = ["nvidia"];
-12
nix/modules/hardware/profiles/base.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.profiles.base.enable = lib.mkEnableOption "Base common hardware configuration"; 7 - 8 - config = lib.mkIf config.myHardware.profiles.base.enable { 9 - hardware.enableAllFirmware = true; 10 - services.fstrim.enable = true; 11 - }; 12 - }
-64
nix/modules/hardware/profiles/laptop.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myHardware.profiles.laptop.enable = lib.mkEnableOption "Laptop hardware configuration."; 7 - 8 - config = lib.mkMerge [ 9 - (lib.mkIf config.myHardware.profiles.laptop.enable 10 - { 11 - boot.kernel.sysctl."kernel.nmi_watchdog" = lib.mkDefault 0; 12 - 13 - services = { 14 - tuned = { 15 - enable = lib.mkDefault true; 16 - settings.dynamic_tuning = true; 17 - }; 18 - 19 - upower.enable = true; 20 - }; 21 - }) 22 - 23 - (lib.mkIf ((lib.elem "kvm-intel" config.boot.kernelModules) && config.myHardware.profiles.laptop.enable) { 24 - services.thermald.enable = true; 25 - }) 26 - 27 - (lib.mkIf ((lib.elem "nvidia" config.services.xserver.videoDrivers) && config.myHardware.profiles.laptop.enable) { 28 - hardware.nvidia = { 29 - dynamicBoost.enable = lib.mkDefault true; 30 - 31 - powerManagement = { 32 - enable = true; 33 - finegrained = true; 34 - }; 35 - 36 - prime.offload = { 37 - # Remember to define nvidiaBusId and intelBusId/amdBusId in config.hardware.nvidia.prime. 38 - enable = true; 39 - enableOffloadCmd = true; 40 - }; 41 - }; 42 - 43 - specialisation.nvidia-sync.configuration = { 44 - environment.etc."specialisation".text = "nvidia-sync"; 45 - 46 - hardware.nvidia = { 47 - powerManagement = { 48 - enable = lib.mkForce false; 49 - finegrained = lib.mkForce false; 50 - }; 51 - 52 - prime = { 53 - offload = { 54 - enable = lib.mkForce false; 55 - enableOffloadCmd = lib.mkForce false; 56 - }; 57 - 58 - sync.enable = lib.mkForce true; 59 - }; 60 - }; 61 - }; 62 - }) 63 - ]; 64 - }
-75
nix/modules/nixos/base.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: { 8 - options.myNixOS.base.enable = lib.mkEnableOption "base system configuration"; 9 - 10 - config = lib.mkIf config.myNixOS.base.enable { 11 - environment = { 12 - etc."nixos".source = self; 13 - 14 - systemPackages = with pkgs; [ 15 - (inxi.override {withRecommends = true;}) 16 - helix 17 - lm_sensors 18 - python314 # For ansible 19 - rclone 20 - wget 21 - zellij 22 - ]; 23 - 24 - variables = { 25 - inherit (config.myNixOS) FLAKE; 26 - NH_FLAKE = config.myNixOS.FLAKE; 27 - }; 28 - }; 29 - 30 - programs = { 31 - direnv = { 32 - enable = true; 33 - nix-direnv.enable = true; 34 - silent = true; 35 - }; 36 - 37 - git.enable = true; 38 - htop.enable = true; 39 - nh.enable = true; 40 - }; 41 - 42 - networking.networkmanager.enable = true; 43 - security.sudo-rs.enable = true; 44 - 45 - services = { 46 - openssh = { 47 - enable = true; 48 - openFirewall = true; 49 - settings.PasswordAuthentication = false; 50 - }; 51 - 52 - timesyncd.enable = true; 53 - }; 54 - 55 - # Decrypt sops secrets using the host's ssh ed25519 host key (converted 56 - # to age at decrypt time). Each host's pubkey lives in keys/ and is 57 - # listed in .sops.yaml; per-secret declarations live in each host's 58 - # secrets.nix. 59 - sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"]; 60 - 61 - system.configurationRevision = self.rev or self.dirtyRev or null; 62 - 63 - systemd = { 64 - # Attempt to limp to accessible state on failure. 65 - coredump.enable = false; 66 - enableEmergencyMode = false; 67 - }; 68 - 69 - myNixOS = { 70 - profiles.performance.enable = true; 71 - programs.njust.enable = true; 72 - services.fail2ban.enable = true; 73 - }; 74 - }; 75 - }
-7
nix/modules/nixos/flake-url.nix
··· 1 - {lib, ...}: { 2 - options.myNixOS.FLAKE = lib.mkOption { 3 - type = lib.types.str; 4 - default = "github:alyraffauf/cute.haus"; 5 - description = "Default flake URL for this NixOS configuration."; 6 - }; 7 - }
-62
nix/modules/nixos/known-hosts.nix
··· 1 - # Host-key pinning for ssh between hosts. 2 - {self, ...}: let 3 - tnet = "narwhal-snapper.ts.net"; 4 - pub = host: "${self}/keys/root_${host}.pub"; 5 - in { 6 - programs.ssh.knownHosts = { 7 - snowpoint = { 8 - hostNames = ["snowpoint" "snowpoint.local" "snowpoint.${tnet}" "dewford" "dewford.local" "dewford.${tnet}"]; 9 - publicKeyFile = pub "snowpoint"; 10 - }; 11 - 12 - fallarbor = { 13 - hostNames = ["fallarbor" "fallarbor.local" "fallarbor.${tnet}"]; 14 - publicKeyFile = pub "fallarbor"; 15 - }; 16 - 17 - fortree = { 18 - hostNames = ["fortree" "fortree.local" "fortree.${tnet}"]; 19 - publicKeyFile = pub "fortree"; 20 - }; 21 - 22 - jubilife = { 23 - hostNames = ["jubilife" "jubilife.local" "jubilife.${tnet}" "lilycove" "lilycove.local" "lilycove.${tnet}"]; 24 - publicKeyFile = pub "jubilife"; 25 - }; 26 - 27 - pastoria = { 28 - hostNames = ["pastoria" "pastoria.local" "pastoria.${tnet}"]; 29 - publicKeyFile = pub "pastoria"; 30 - }; 31 - 32 - eterna = { 33 - hostNames = ["eterna" "eterna.local" "eterna.${tnet}" "mauville" "mauville.local" "mauville.${tnet}"]; 34 - publicKeyFile = pub "eterna"; 35 - }; 36 - 37 - solaceon = { 38 - hostNames = ["solaceon" "solaceon.local" "solaceon.${tnet}" "mossdeep" "mossdeep.local" "mossdeep.${tnet}"]; 39 - publicKeyFile = pub "solaceon"; 40 - }; 41 - 42 - petalburg = { 43 - hostNames = ["petalburg" "petalburg.local" "petalburg.${tnet}"]; 44 - publicKeyFile = pub "petalburg"; 45 - }; 46 - 47 - rustboro = { 48 - hostNames = ["rustboro" "rustboro.local" "rustboro.${tnet}"]; 49 - publicKeyFile = pub "rustboro"; 50 - }; 51 - 52 - slateport = { 53 - hostNames = ["slateport" "slateport.local" "slateport.${tnet}"]; 54 - publicKeyFile = pub "slateport"; 55 - }; 56 - 57 - sootopolis = { 58 - hostNames = ["sootopolis" "sootopolis.local" "sootopolis.${tnet}"]; 59 - publicKeyFile = pub "sootopolis"; 60 - }; 61 - }; 62 - }
-60
nix/modules/nixos/profiles/arr.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.arr = { 7 - enable = lib.mkEnableOption "*arr services"; 8 - 9 - dataDir = lib.mkOption { 10 - type = lib.types.str; 11 - default = "/var/lib"; 12 - description = "The directory where *arr stores its data files."; 13 - }; 14 - }; 15 - 16 - config = lib.mkMerge [ 17 - (lib.mkIf config.myNixOS.profiles.arr.enable { 18 - services = { 19 - bazarr = { 20 - enable = true; 21 - dataDir = "${config.myNixOS.profiles.arr.dataDir}/bazarr"; 22 - openFirewall = true; # Port: 6767 23 - }; 24 - 25 - lidarr = { 26 - enable = true; 27 - dataDir = "${config.myNixOS.profiles.arr.dataDir}/lidarr/.config/Lidarr"; 28 - openFirewall = true; # Port: 8686 29 - }; 30 - 31 - prowlarr = { 32 - enable = true; 33 - # dataDir = "${config.myNixOS.profiles.arr.dataDir}/prowlarr"; 34 - openFirewall = true; # Port: 9696 35 - }; 36 - 37 - radarr = { 38 - enable = true; 39 - dataDir = "${config.myNixOS.profiles.arr.dataDir}/radarr/.config/Radarr/"; 40 - openFirewall = true; # Port: 7878 41 - }; 42 - 43 - sonarr = { 44 - enable = true; 45 - dataDir = "${config.myNixOS.profiles.arr.dataDir}/sonarr/.config/NzbDrone/"; 46 - openFirewall = true; # Port: 8989 47 - }; 48 - }; 49 - 50 - systemd = { 51 - tmpfiles.rules = [ 52 - "d ${config.services.lidarr.dataDir} 0755 lidarr lidarr" 53 - "d ${config.services.radarr.dataDir} 0755 radarr radarr" 54 - "d ${config.services.readarr.dataDir} 0755 readarr readarr" 55 - "d ${config.services.sonarr.dataDir} 0755 sonarr sonarr" 56 - ]; 57 - }; 58 - }) 59 - ]; 60 - }
-48
nix/modules/nixos/profiles/autoUpgrade.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.autoUpgrade = { 7 - enable = lib.mkEnableOption "auto-upgrade system"; 8 - 9 - operation = lib.mkOption { 10 - type = lib.types.str; 11 - default = "switch"; 12 - description = "Operation to perform on auto-upgrade. Can be 'boot', 'switch', or 'test'."; 13 - }; 14 - 15 - dates = lib.mkOption { 16 - type = lib.types.str; 17 - default = "02:00"; 18 - description = '' 19 - systemd OnCalendar expression for when the upgrade fires. 20 - ''; 21 - }; 22 - 23 - randomizedDelaySec = lib.mkOption { 24 - type = lib.types.str; 25 - default = "0"; 26 - description = '' 27 - Random delay added on top of `dates`. 28 - ''; 29 - }; 30 - }; 31 - 32 - config = lib.mkIf config.myNixOS.profiles.autoUpgrade.enable { 33 - system.autoUpgrade = { 34 - inherit (config.myNixOS.profiles.autoUpgrade) operation dates randomizedDelaySec; 35 - 36 - enable = true; 37 - allowReboot = true; 38 - flags = ["--accept-flake-config"]; 39 - flake = config.myNixOS.FLAKE; 40 - persistent = true; 41 - 42 - rebootWindow = { 43 - lower = "02:00"; 44 - upper = "06:00"; 45 - }; 46 - }; 47 - }; 48 - }
-110
nix/modules/nixos/profiles/b2-mounts.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: let 8 - cfg = config.myNixOS.profiles.b2-mounts; 9 - 10 - b2Options = [ 11 - "allow_other" 12 - "args2env" 13 - "cache-dir=${cfg.cacheDir}" 14 - "config=${config.sops.secrets.b2-mount-rclone.path}" 15 - "dir-cache-time=1h" 16 - "nodev" 17 - "nofail" 18 - "vfs-cache-mode=full" 19 - "vfs-write-back=10s" 20 - "x-systemd.after=network-online.target" 21 - "x-systemd.automount" 22 - ]; 23 - 24 - b2ProfileOptions = { 25 - audio = [ 26 - "buffer-size=128M" 27 - "vfs-cache-max-age=168h" 28 - "vfs-cache-max-size=${cfg.audioCacheSize}" 29 - "vfs-read-ahead=${cfg.audioReadAhead}" 30 - ]; 31 - 32 - video = [ 33 - "buffer-size=512M" 34 - "vfs-cache-max-age=336h" 35 - "vfs-cache-max-size=${cfg.videoCacheSize}" 36 - "vfs-read-ahead=${cfg.videoReadAhead}" 37 - ]; 38 - }; 39 - 40 - mkB2Mount = name: remote: profile: { 41 - "/mnt/Backblaze/${name}" = { 42 - device = "b2:${remote}"; 43 - fsType = "rclone"; 44 - options = b2Options ++ b2ProfileOptions.${profile}; 45 - }; 46 - }; 47 - 48 - allShares = { 49 - Anime = mkB2Mount "Anime" "aly-anime" "video"; 50 - Audiobooks = mkB2Mount "Audiobooks" "aly-audiobooks" "audio"; 51 - Movies = mkB2Mount "Movies" "aly-movies" "video"; 52 - Music = mkB2Mount "Music" "aly-music" "audio"; 53 - Shows = mkB2Mount "Shows" "aly-shows" "video"; 54 - }; 55 - in { 56 - options.myNixOS.profiles.b2-mounts = { 57 - enable = lib.mkEnableOption "Backblaze B2 rclone filesystem mounts"; 58 - 59 - cacheDir = lib.mkOption { 60 - description = "Directory for rclone VFS cache."; 61 - example = "/mnt/Data/.rclone-cache"; 62 - type = lib.types.str; 63 - }; 64 - 65 - audioCacheSize = lib.mkOption { 66 - description = "VFS cache max size for audio shares."; 67 - default = "15G"; 68 - type = lib.types.str; 69 - }; 70 - 71 - videoCacheSize = lib.mkOption { 72 - description = "VFS cache max size for video shares."; 73 - default = "50G"; 74 - type = lib.types.str; 75 - }; 76 - 77 - audioReadAhead = lib.mkOption { 78 - description = "VFS read-ahead for audio shares."; 79 - default = "1G"; 80 - type = lib.types.str; 81 - }; 82 - 83 - videoReadAhead = lib.mkOption { 84 - description = "VFS read-ahead for video shares."; 85 - default = "3G"; 86 - type = lib.types.str; 87 - }; 88 - 89 - shares = lib.mkOption { 90 - description = "Which B2 shares to mount."; 91 - default = ["Anime" "Audiobooks" "Movies" "Music" "Shows"]; 92 - type = lib.types.listOf (lib.types.enum ["Anime" "Audiobooks" "Movies" "Music" "Shows"]); 93 - }; 94 - }; 95 - 96 - config = lib.mkIf cfg.enable { 97 - sops.secrets.b2-mount-rclone = { 98 - sopsFile = "${self}/secrets/b2.yaml"; 99 - key = "rclone_config"; 100 - }; 101 - 102 - environment.systemPackages = [pkgs.rclone]; 103 - fileSystems = builtins.foldl' (a: b: a // b) {} (builtins.attrValues (builtins.intersectAttrs (builtins.listToAttrs (map (s: { 104 - name = s; 105 - value = null; 106 - }) 107 - cfg.shares)) 108 - allShares)); 109 - }; 110 - }
-308
nix/modules/nixos/profiles/backups.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: let 8 - backupDestination = "rclone:b2:aly-backups/${config.networking.hostName}"; 9 - mkRepo = service: "${backupDestination}/${service}"; 10 - stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 11 - start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 12 - restic = { 13 - extraBackupArgs = [ 14 - "--cleanup-cache" 15 - "--compression max" 16 - "--no-scan" 17 - ]; 18 - inhibitsSleep = true; 19 - initialize = true; 20 - passwordFile = config.sops.secrets.restic-passwd.path; 21 - pruneOpts = [ 22 - "--keep-daily 7" 23 - "--keep-weekly 4" 24 - "--keep-monthly 3" 25 - ]; 26 - rcloneConfigFile = config.sops.secrets.rclone-b2.path; 27 - timerConfig = { 28 - OnCalendar = "daily"; 29 - Persistent = true; 30 - RandomizedDelaySec = "3h"; 31 - }; 32 - }; 33 - in { 34 - options.myNixOS.profiles.backups = { 35 - enable = lib.mkEnableOption "automatically back up enabled services to b2"; 36 - 37 - extraJobs = lib.mkOption { 38 - description = "Additional restic backup jobs merged into the base defaults."; 39 - default = {}; 40 - type = lib.types.attrsOf (lib.types.submodule (_: { 41 - options = { 42 - paths = lib.mkOption { 43 - type = lib.types.listOf lib.types.path; 44 - description = "Paths to back up."; 45 - }; 46 - repository = lib.mkOption { 47 - type = lib.types.str; 48 - description = "Restic repository URL."; 49 - }; 50 - backupPrepareCommand = lib.mkOption { 51 - type = lib.types.nullOr lib.types.str; 52 - default = null; 53 - }; 54 - backupCleanupCommand = lib.mkOption { 55 - type = lib.types.nullOr lib.types.str; 56 - default = null; 57 - }; 58 - exclude = lib.mkOption { 59 - type = lib.types.listOf lib.types.str; 60 - default = []; 61 - }; 62 - }; 63 - })); 64 - }; 65 - }; 66 - 67 - config = lib.mkIf config.myNixOS.profiles.backups.enable { 68 - sops.secrets = { 69 - restic-passwd = { 70 - sopsFile = "${self}/secrets/restic.yaml"; 71 - key = "PASSWORD"; 72 - }; 73 - 74 - rclone-b2 = { 75 - sopsFile = "${self}/secrets/b2.yaml"; 76 - key = "rclone_config"; 77 - }; 78 - }; 79 - 80 - services.restic.backups = 81 - { 82 - audiobookshelf = lib.mkIf config.services.audiobookshelf.enable ( 83 - restic 84 - // { 85 - backupCleanupCommand = start "audiobookshelf"; 86 - backupPrepareCommand = stop "audiobookshelf"; 87 - paths = [config.services.audiobookshelf.dataDir]; 88 - repository = mkRepo "audiobookshelf"; 89 - } 90 - ); 91 - 92 - bazarr = lib.mkIf config.services.bazarr.enable ( 93 - restic 94 - // { 95 - backupCleanupCommand = start "bazarr"; 96 - backupPrepareCommand = stop "bazarr"; 97 - paths = [config.services.bazarr.dataDir]; 98 - repository = mkRepo "bazarr"; 99 - } 100 - ); 101 - 102 - couchdb = lib.mkIf config.services.couchdb.enable ( 103 - restic 104 - // { 105 - backupCleanupCommand = start "couchdb"; 106 - backupPrepareCommand = stop "couchdb"; 107 - paths = [config.services.couchdb.databaseDir]; 108 - repository = mkRepo "couchdb"; 109 - } 110 - ); 111 - 112 - forgejo = lib.mkIf (config.services.forgejo.enable && config.services.forgejo.settings.storage.STORAGE_TYPE != "minio") ( 113 - restic 114 - // { 115 - paths = [config.services.forgejo.stateDir]; 116 - repository = mkRepo "forgejo"; 117 - } 118 - ); 119 - 120 - homebridge = lib.mkIf config.services.homebridge.enable ( 121 - restic 122 - // { 123 - paths = ["/var/lib/homebridge"]; 124 - repository = "rclone:b2:aly-backups/${config.networking.hostName}/homebridge"; 125 - } 126 - ); 127 - 128 - immich = lib.mkIf config.services.immich.enable ( 129 - restic 130 - // { 131 - backupCleanupCommand = start "immich-server"; 132 - backupPrepareCommand = stop "immich-server"; 133 - 134 - paths = [ 135 - "${config.services.immich.mediaLocation}/library" 136 - "${config.services.immich.mediaLocation}/profile" 137 - "${config.services.immich.mediaLocation}/upload" 138 - "${config.services.immich.mediaLocation}/backups" 139 - ]; 140 - 141 - repository = mkRepo "immich"; 142 - } 143 - ); 144 - 145 - jellyfin = lib.mkIf config.services.jellyfin.enable ( 146 - restic 147 - // { 148 - backupCleanupCommand = start "jellyfin"; 149 - backupPrepareCommand = stop "jellyfin"; 150 - paths = [config.services.jellyfin.dataDir]; 151 - repository = mkRepo "jellyfin"; 152 - } 153 - ); 154 - 155 - k3s = lib.mkIf (config.services.k3s.enable && config.services.k3s.role == "server") ( 156 - restic 157 - // { 158 - backupPrepareCommand = "${config.services.k3s.package}/bin/k3s etcd-snapshot save"; 159 - paths = [ 160 - "/var/lib/rancher/k3s/server/db/snapshots" 161 - "/var/lib/rancher/k3s/server/cred" 162 - "/var/lib/rancher/k3s/server/tls" 163 - ]; 164 - repository = mkRepo "k3s"; 165 - } 166 - ); 167 - 168 - lidarr = lib.mkIf config.services.lidarr.enable ( 169 - restic 170 - // { 171 - backupCleanupCommand = start "lidarr"; 172 - backupPrepareCommand = stop "lidarr"; 173 - paths = [config.services.lidarr.dataDir]; 174 - repository = mkRepo "lidarr"; 175 - } 176 - ); 177 - 178 - ombi = lib.mkIf config.services.ombi.enable ( 179 - restic 180 - // { 181 - backupCleanupCommand = start "ombi"; 182 - backupPrepareCommand = stop "ombi"; 183 - paths = [config.services.ombi.dataDir]; 184 - repository = mkRepo "ombi"; 185 - } 186 - ); 187 - 188 - pds = lib.mkIf config.services.bluesky-pds.enable ( 189 - restic 190 - // { 191 - backupCleanupCommand = start "bluesky-pds"; 192 - backupPrepareCommand = stop "bluesky-pds"; 193 - paths = [config.services.bluesky-pds.settings.PDS_DATA_DIRECTORY]; 194 - repository = mkRepo "pds"; 195 - } 196 - ); 197 - 198 - plex = lib.mkIf config.services.plex.enable ( 199 - restic 200 - // { 201 - backupCleanupCommand = start "plex"; 202 - backupPrepareCommand = stop "plex"; 203 - exclude = ["${config.services.plex.dataDir}/Plex Media Server/Plug-in Support/Databases"]; 204 - paths = [config.services.plex.dataDir]; 205 - repository = mkRepo "plex"; 206 - } 207 - ); 208 - 209 - postgresql = lib.mkIf config.services.postgresql.enable ( 210 - restic 211 - // { 212 - backupCleanupCommand = start "postgresql"; 213 - backupPrepareCommand = stop "postgresql"; 214 - paths = [config.services.postgresql.dataDir]; 215 - repository = mkRepo "postgresql"; 216 - } 217 - ); 218 - 219 - prowlarr = lib.mkIf config.services.prowlarr.enable ( 220 - restic 221 - // { 222 - backupCleanupCommand = start "prowlarr"; 223 - backupPrepareCommand = stop "prowlarr"; 224 - paths = [config.services.prowlarr.dataDir]; 225 - repository = mkRepo "prowlarr"; 226 - } 227 - ); 228 - 229 - qbittorrent = lib.mkIf config.myNixOS.services.qbittorrent.enable ( 230 - restic 231 - // { 232 - backupCleanupCommand = start "qbittorrent"; 233 - backupPrepareCommand = stop "qbittorrent"; 234 - paths = [config.myNixOS.services.qbittorrent.dataDir]; 235 - repository = mkRepo "qbittorrent"; 236 - } 237 - ); 238 - 239 - radarr = lib.mkIf config.services.radarr.enable ( 240 - restic 241 - // { 242 - backupCleanupCommand = start "radarr"; 243 - backupPrepareCommand = stop "radarr"; 244 - paths = [config.services.radarr.dataDir]; 245 - repository = mkRepo "radarr"; 246 - } 247 - ); 248 - 249 - readarr = lib.mkIf config.services.readarr.enable ( 250 - restic 251 - // { 252 - backupCleanupCommand = start "readarr"; 253 - backupPrepareCommand = stop "readarr"; 254 - paths = [config.services.readarr.dataDir]; 255 - repository = mkRepo "readarr"; 256 - } 257 - ); 258 - 259 - sonarr = lib.mkIf config.services.sonarr.enable ( 260 - restic 261 - // { 262 - backupCleanupCommand = start "sonarr"; 263 - backupPrepareCommand = stop "sonarr"; 264 - paths = [config.services.sonarr.dataDir]; 265 - repository = mkRepo "sonarr"; 266 - } 267 - ); 268 - 269 - tautulli = lib.mkIf config.services.tautulli.enable ( 270 - restic 271 - // { 272 - backupCleanupCommand = start "tautulli"; 273 - backupPrepareCommand = stop "tautulli"; 274 - paths = [config.services.tautulli.dataDir]; 275 - repository = mkRepo "tautulli"; 276 - } 277 - ); 278 - 279 - uptime-kuma = lib.mkIf config.services.uptime-kuma.enable ( 280 - restic 281 - // { 282 - backupCleanupCommand = start "uptime-kuma"; 283 - backupPrepareCommand = stop "uptime-kuma"; 284 - paths = ["/var/lib/uptime-kuma"]; 285 - repository = mkRepo "uptime-kuma"; 286 - } 287 - ); 288 - 289 - vaultwarden = lib.mkIf config.services.vaultwarden.enable ( 290 - restic 291 - // { 292 - backupCleanupCommand = start "vaultwarden"; 293 - backupPrepareCommand = stop "vaultwarden"; 294 - paths = ["/var/lib/vaultwarden"]; 295 - repository = mkRepo "vaultwarden"; 296 - } 297 - ); 298 - } 299 - // lib.mapAttrs (_: job: 300 - restic 301 - // { 302 - inherit (job) paths repository exclude; 303 - backupPrepareCommand = lib.mkIf (job.backupPrepareCommand != null) job.backupPrepareCommand; 304 - backupCleanupCommand = lib.mkIf (job.backupCleanupCommand != null) job.backupCleanupCommand; 305 - }) 306 - config.myNixOS.profiles.backups.extraJobs; 307 - }; 308 - }
-107
nix/modules/nixos/profiles/btrfs.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: let 7 - # Compute a list of Btrfs file systems (with mountPoint and device) 8 - btrfsFSDevices = let 9 - # Helper: does a device already appear in the accumulator? 10 - isDeviceInList = list: device: 11 - builtins.any (e: e.device == device) list; 12 - 13 - # Helper: keep only the first occurrence of each device 14 - uniqueDeviceList = lib.foldl' ( 15 - acc: e: 16 - if isDeviceInList acc e.device 17 - then acc 18 - else acc ++ [e] 19 - ) []; 20 - in 21 - uniqueDeviceList ( 22 - lib.mapAttrsToList (_: fs: { 23 - inherit (fs) mountPoint device; 24 - }) 25 - (lib.filterAttrs (_: fs: fs.fsType == "btrfs") config.fileSystems) 26 - ); 27 - 28 - # Create beesd.filesystems attrset keyed by device basename, with spec = device path 29 - beesdConfig = lib.listToAttrs (map (fs: { 30 - name = lib.strings.sanitizeDerivationName (baseNameOf fs.device); 31 - 32 - value = { 33 - hashTableSizeMB = 2048; 34 - spec = fs.device; 35 - verbosity = "info"; 36 - 37 - extraOptions = [ 38 - "--loadavg-target" 39 - "1.0" 40 - "--thread-factor" 41 - "0.50" 42 - ]; 43 - }; 44 - }) 45 - btrfsFSDevices); 46 - 47 - # Check if a btrfs /home entry exists 48 - hasHomeSubvolume = 49 - lib.hasAttr "/home" config.fileSystems 50 - && config.fileSystems."/home".fsType == "btrfs"; 51 - in { 52 - options.myNixOS.profiles.btrfs = { 53 - enable = lib.mkEnableOption "btrfs filesystem configuration"; 54 - deduplicate = lib.mkEnableOption "deduplicate btrfs filesystems"; 55 - }; 56 - 57 - config = lib.mkIf config.myNixOS.profiles.btrfs.enable { 58 - boot.supportedFilesystems = ["btrfs"]; 59 - environment.systemPackages = lib.optionals config.services.xserver.enable [pkgs.snapper-gui]; 60 - 61 - services = lib.mkIf (btrfsFSDevices != []) { 62 - beesd.filesystems = lib.mkIf config.myNixOS.profiles.btrfs.deduplicate beesdConfig; 63 - btrfs.autoScrub.enable = true; 64 - 65 - snapper = { 66 - configs.home = lib.mkIf hasHomeSubvolume { 67 - ALLOW_GROUPS = ["users"]; 68 - FSTYPE = "btrfs"; 69 - SUBVOLUME = "/home"; 70 - TIMELINE_CLEANUP = true; 71 - TIMELINE_CREATE = true; 72 - }; 73 - 74 - filters = '' 75 - -.bash_profile 76 - -.bashrc 77 - -.cache 78 - -.config 79 - -.librewolf 80 - -.local 81 - -.mozilla 82 - -.nix-profile 83 - -.pki 84 - -.share 85 - -.snapshots 86 - -.thunderbird 87 - -.zshrc 88 - ''; 89 - 90 - persistentTimer = true; 91 - }; 92 - }; 93 - 94 - myNixOS.programs.njust.recipes.btrfs = lib.mkIf (btrfsFSDevices != []) '' 95 - # List snapper snapshots 96 - [group('btrfs')] 97 - snapshots config="home": 98 - snapper -c {{config}} list 99 - 100 - # Create a manual snapshot 101 - [group('btrfs')] 102 - create-snapshot config="home" desc="manual": 103 - @echo "Creating snapshot '{{desc}}' for config {{config}}" 104 - snapper -c {{config}} create --description "{{desc}}" 105 - ''; 106 - }; 107 - }
+8 -17
nix/modules/nixos/profiles/data-share.nix nix/nixos/profiles/data-share.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myNixOS.profiles.data-share.enable = lib.mkEnableOption "data share"; 8 - 9 - config = lib.mkIf config.myNixOS.profiles.data-share.enable { 1 + _: { 2 + flake.modules.nixos.data-share = { 3 + config, 4 + pkgs, 5 + ... 6 + }: { 10 7 assertions = [ 11 8 { 12 9 assertion = config.services.tailscale.enable; ··· 16 13 17 14 environment.systemPackages = [pkgs.nfs-utils]; 18 15 19 - fileSystems = let 16 + fileSystems."/mnt/Data" = { 20 17 fsType = "nfs"; 18 + device = "jubilife:/mnt/Data"; 21 19 22 20 options = [ 23 21 "default" ··· 33 31 "x-systemd.idle-timeout=60" 34 32 "x-systemd.mount-timeout=5s" 35 33 ]; 36 - in { 37 - "/mnt/Data" = { 38 - inherit options fsType; 39 - device = "jubilife:/mnt/Data"; 40 - }; 41 34 }; 42 - 43 - myNixOS.services.cachefilesd.enable = true; 44 35 }; 45 36 }
+6 -10
nix/modules/nixos/profiles/iso.nix nix/nixos/profiles/iso.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: { 8 - options.myNixOS.profiles.iso.enable = lib.mkEnableOption "base system configuration for iso environments"; 9 - 10 - config = lib.mkIf config.myNixOS.profiles.iso.enable { 1 + {self, ...}: { 2 + flake.modules.nixos.iso = { 3 + lib, 4 + pkgs, 5 + ... 6 + }: { 11 7 boot = { 12 8 kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; 13 9
-163
nix/modules/nixos/profiles/k3s.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: let 8 - cfg = config.myNixOS.profiles.k3s; 9 - in { 10 - options.myNixOS.profiles.k3s = { 11 - enable = lib.mkEnableOption "k3s cluster node"; 12 - 13 - role = lib.mkOption { 14 - type = lib.types.enum ["server" "agent"]; 15 - default = "server"; 16 - description = "k3s role for this node."; 17 - }; 18 - 19 - clusterInit = lib.mkOption { 20 - type = lib.types.bool; 21 - default = false; 22 - description = '' 23 - Whether this node initializes the cluster's etcd. Exactly one node 24 - in the cluster should set this. Other servers join via `serverAddr`. 25 - ''; 26 - }; 27 - 28 - serverAddr = lib.mkOption { 29 - type = lib.types.nullOr lib.types.str; 30 - default = null; 31 - example = "https://solaceon:6443"; 32 - description = '' 33 - URL of the cluster's first server. Required for joining nodes 34 - (non-init servers and agents); null on the cluster-init node. 35 - ''; 36 - }; 37 - 38 - tlsSans = lib.mkOption { 39 - type = lib.types.listOf lib.types.str; 40 - default = ["solaceon" "eterna" "pastoria"]; 41 - example = ["solaceon" "eterna" "pastoria"]; 42 - description = "Hostnames to include as TLS SANs on this node's certs."; 43 - }; 44 - 45 - zone = lib.mkOption { 46 - type = lib.types.nullOr lib.types.str; 47 - default = null; 48 - example = "cloud-hetzner"; 49 - description = '' 50 - Failure domain for this node. Emitted as the standard k8s 51 - topology.kubernetes.io/zone label so Longhorn replica scheduling and 52 - topologySpreadConstraints can spread workloads across providers and 53 - on-prem machines. Current values in use: home, cloud-hetzner, 54 - cloud-netcup. 55 - ''; 56 - }; 57 - 58 - ingress = lib.mkEnableOption '' 59 - cute.haus/ingress=true node label. Traefik runs on nodes with this 60 - label; set true on any node that should serve public HTTP(S)/SSH 61 - ingress (needs ports 80/443/2222 open at the firewall). 62 - ''; 63 - }; 64 - 65 - config = lib.mkIf cfg.enable { 66 - sops.secrets.k3s = { 67 - sopsFile = "${self}/secrets/k3s.yaml"; 68 - key = "TOKEN"; 69 - }; 70 - 71 - # systemd-oomd fights kubelet's eviction manager 72 - systemd.oomd.enable = lib.mkForce false; 73 - 74 - # Traefik DaemonSet binds these hostPorts on every ingress-labeled node. 75 - # 80/443 = HTTP(S), 2222 = forgejo SSH (see k8s/helmfile.yaml traefik 76 - # ports config). 77 - networking.firewall.allowedTCPPorts = lib.mkIf cfg.ingress [80 443 2222]; 78 - 79 - services = { 80 - k3s = { 81 - enable = true; 82 - inherit (cfg) role clusterInit; 83 - serverAddr = lib.mkIf (cfg.serverAddr != null) cfg.serverAddr; 84 - tokenFile = config.sops.secrets.k3s.path; 85 - extraFlags = 86 - ["--flannel-iface=tailscale0"] 87 - ++ lib.optionals (cfg.role == "server") ( 88 - [ 89 - "--service-node-port-range=8000-32767" 90 - "--disable=traefik" 91 - "--disable=servicelb" 92 - ] 93 - ++ map (san: "--tls-san=${san}") cfg.tlsSans 94 - ) 95 - ++ lib.optionals cfg.clusterInit [ 96 - "--write-kubeconfig-mode=644" 97 - ] 98 - ++ lib.optionals (cfg.zone != null) [ 99 - "--node-label=topology.kubernetes.io/zone=${cfg.zone}" 100 - ] 101 - ++ lib.optionals cfg.ingress [ 102 - "--node-label=cute.haus/ingress=true" 103 - ]; 104 - }; 105 - 106 - # Longhorn prereq 107 - openiscsi = { 108 - enable = true; 109 - name = "iqn.2026-05.haus.cute:${config.networking.hostName}"; 110 - }; 111 - }; 112 - 113 - environment.systemPackages = with pkgs; [ 114 - helmfile 115 - kubernetes-helm 116 - nfs-utils # Longhorn backup target uses NFS client 117 - ]; 118 - 119 - systemd = { 120 - # Longhorn instance-manager looks for binaries in /usr/local/bin 121 - tmpfiles.rules = [ 122 - "L+ /usr/local/bin - - - - /run/current-system/sw/bin/" 123 - ]; 124 - 125 - services = { 126 - # Block k3s startup until tailscale0 has its IP — flannel-iface=tailscale0 127 - # otherwise races and etcd peer setup fails on cold boot. 128 - k3s = { 129 - after = ["tailscaled.service"]; 130 - wants = ["tailscaled.service"]; 131 - serviceConfig.ExecStartPre = pkgs.writeShellScript "wait-tailscale0" '' 132 - until ${pkgs.iproute2}/bin/ip -4 addr show tailscale0 | grep -q inet; do 133 - ${pkgs.coreutils}/bin/sleep 1 134 - done 135 - ''; 136 - }; 137 - 138 - # Without this, reboots hang ~5min: k3s stops, the longhorn engine pod 139 - # on this node dies → kernel iSCSI session sees a connection error, 140 - # udev spawns scsi_id against the dead device, scsi_id hangs until its 141 - # 3min timeout, then systemd-shutdown SIGKILLs everything. Explicitly 142 - # logging out before iscsid stops lets the kernel close the session 143 - # gracefully. Ordering: starts after iscsid + before k3s, so on 144 - # shutdown it stops AFTER k3s (CSI volumes already detached) and 145 - # BEFORE iscsid (sessions still managable). 146 - iscsi-logout = { 147 - description = "Log out iSCSI sessions cleanly at shutdown"; 148 - after = ["iscsid.service"]; 149 - before = ["k3s.service"]; 150 - requires = ["iscsid.service"]; 151 - wantedBy = ["multi-user.target"]; 152 - serviceConfig = { 153 - Type = "oneshot"; 154 - RemainAfterExit = true; 155 - ExecStart = "${pkgs.coreutils}/bin/true"; 156 - ExecStop = "-${pkgs.openiscsi}/bin/iscsiadm -m node -u"; 157 - TimeoutStopSec = "30s"; 158 - }; 159 - }; 160 - }; 161 - }; 162 - }; 163 - }
+8 -17
nix/modules/nixos/profiles/media-share.nix nix/nixos/profiles/media-share.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myNixOS.profiles.media-share.enable = lib.mkEnableOption "media share"; 8 - 9 - config = lib.mkIf config.myNixOS.profiles.media-share.enable { 1 + _: { 2 + flake.modules.nixos.media-share = { 3 + config, 4 + pkgs, 5 + ... 6 + }: { 10 7 assertions = [ 11 8 { 12 9 assertion = config.services.tailscale.enable; ··· 16 13 17 14 environment.systemPackages = [pkgs.nfs-utils]; 18 15 19 - fileSystems = let 16 + fileSystems."/mnt/Media" = { 20 17 fsType = "nfs"; 18 + device = "jubilife:/mnt/Media"; 21 19 22 20 options = [ 23 21 "default" ··· 33 31 "x-systemd.idle-timeout=60" 34 32 "x-systemd.mount-timeout=5s" 35 33 ]; 36 - in { 37 - "/mnt/Media" = { 38 - inherit options fsType; 39 - device = "jubilife:/mnt/Media"; 40 - }; 41 34 }; 42 - 43 - myNixOS.services.cachefilesd.enable = true; 44 35 }; 45 36 }
+2 -10
nix/modules/nixos/profiles/performance.nix nix/nixos/profiles/performance.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.performance.enable = lib.mkEnableOption "performance tunings"; 7 - 8 - config = lib.mkIf config.myNixOS.profiles.performance.enable { 1 + _: { 2 + flake.modules.nixos.performance = {lib, ...}: { 9 3 boot.kernel.sysctl = { 10 - # Improved file monitoring 11 4 "fs.file-max" = lib.mkDefault 2097152; 12 5 "fs.inotify.max_user_instances" = lib.mkOverride 100 8192; 13 6 "fs.inotify.max_user_watches" = lib.mkOverride 100 524288; 14 7 }; 15 8 16 9 hardware.block = { 17 - # Default schedulers for rotational and non-rotational devices 18 10 defaultScheduler = "kyber"; 19 11 defaultSchedulerRotational = "bfq"; 20 12
-26
nix/modules/nixos/profiles/swap.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.swap = { 7 - enable = lib.mkEnableOption "swap file"; 8 - 9 - size = lib.mkOption { 10 - default = 8192; 11 - description = "Swap size in megabytes."; 12 - type = lib.types.int; 13 - }; 14 - }; 15 - 16 - config = lib.mkIf config.myNixOS.profiles.swap.enable { 17 - swapDevices = [ 18 - { 19 - device = "/.swap"; 20 - priority = 0; 21 - randomEncryption.enable = true; 22 - inherit (config.myNixOS.profiles.swap) size; 23 - } 24 - ]; 25 - }; 26 - }
-23
nix/modules/nixos/profiles/vps.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.vps.enable = lib.mkEnableOption "vps optimizations"; 7 - 8 - config = lib.mkIf config.myNixOS.profiles.vps.enable { 9 - documentation = { 10 - enable = false; 11 - nixos.enable = false; 12 - }; 13 - 14 - services.journald = { 15 - storage = "persistent"; 16 - 17 - extraConfig = '' 18 - SystemMaxUse=500M 19 - MaxRetentionSec=1week 20 - ''; 21 - }; 22 - }; 23 - }
-15
nix/modules/nixos/profiles/zram.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.profiles.zram.enable = lib.mkEnableOption "zram swap"; 7 - 8 - config = lib.mkIf config.myNixOS.profiles.zram.enable { 9 - zramSwap = { 10 - enable = true; 11 - algorithm = "zstd"; 12 - priority = 100; 13 - }; 14 - }; 15 - }
nix/modules/nixos/programs/lanzaboote.md nix/nixos/programs/lanzaboote.md
+7 -8
nix/modules/nixos/programs/lanzaboote.nix nix/nixos/programs/lanzaboote.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myNixOS.programs.lanzaboote.enable = lib.mkEnableOption "secure boot with lanzaboote"; 1 + {inputs, ...}: { 2 + flake.modules.nixos.lanzaboote = { 3 + lib, 4 + pkgs, 5 + ... 6 + }: { 7 + imports = [inputs.lanzaboote.nixosModules.lanzaboote]; 8 8 9 - config = lib.mkIf config.myNixOS.programs.lanzaboote.enable { 10 9 boot = { 11 10 initrd.systemd.enable = true; 12 11
-123
nix/modules/nixos/programs/nix.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: let 6 - buildMachines = [ 7 - { 8 - hostName = "jubilife"; 9 - maxJobs = 12; 10 - protocol = "ssh-ng"; 11 - speedFactor = 5; 12 - sshKey = "/etc/ssh/ssh_host_ed25519_key"; 13 - sshUser = "nixbuild"; 14 - supportedFeatures = ["nixos-test" "benchmark" "big-parallel" "kvm"]; 15 - systems = ["x86_64-linux"]; 16 - } 17 - ]; 18 - 19 - isBuildMachine = let buildHosts = lib.map (m: m.hostName) buildMachines; in lib.elem config.networking.hostName buildHosts; 20 - in { 21 - options.myNixOS.programs.nix.enable = lib.mkEnableOption "sane nix configuration"; 22 - 23 - config = lib.mkIf config.myNixOS.programs.nix.enable { 24 - nix = { 25 - buildMachines = lib.mkIf config.services.tailscale.enable ( 26 - lib.filter (m: m.hostName != config.networking.hostName) 27 - buildMachines 28 - ); 29 - 30 - distributedBuilds = true; 31 - 32 - gc = { 33 - automatic = true; 34 - 35 - options = 36 - if isBuildMachine 37 - then "--delete-older-than 20d" 38 - else "--delete-older-than 3d"; 39 - 40 - persistent = true; 41 - randomizedDelaySec = "60min"; 42 - }; 43 - 44 - extraOptions = '' 45 - min-free = ${toString (1 * 1024 * 1024 * 1024)} # 1 GiB 46 - max-free = ${toString (5 * 1024 * 1024 * 1024)} # 5 GiB 47 - ''; 48 - 49 - optimise = { 50 - automatic = true; 51 - persistent = true; 52 - randomizedDelaySec = "60min"; 53 - }; 54 - 55 - settings = { 56 - builders-use-substitutes = true; 57 - 58 - experimental-features = [ 59 - "fetch-closure" 60 - "flakes" 61 - "nix-command" 62 - ]; 63 - 64 - substituters = [ 65 - "https://cache.nixos.org/" 66 - "https://alyraffauf.cachix.org" 67 - "https://catppuccin.cachix.org" 68 - "https://chaotic-nyx.cachix.org/" 69 - "https://cutehaus.cachix.org" 70 - "https://nix-community.cachix.org" 71 - ]; 72 - 73 - trusted-public-keys = [ 74 - "alyraffauf.cachix.org-1:GQVrRGfjTtkPGS8M6y7Ik0z4zLt77O0N25ynv2gWzDM=" 75 - "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" 76 - "catppuccin.cachix.org-1:noG/4HkbhJb+lUAdKrph6LaozJvAeEEZj4N732IysmU=" 77 - "chaotic-nyx.cachix.org-1:HfnXSw4pj95iI/n17rIDy40agHj12WfF+Gqk6SonIT8" 78 - "cutehaus.cachix.org-1:KiifTsseQBitoaHH8rkDUDwzyz9akLeOM+K+e2eK8dA=" 79 - "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 80 - ]; 81 - 82 - trusted-users = ["aly" "@admin" "@wheel" "nixbuild"]; 83 - }; 84 - }; 85 - 86 - programs.nix-ld.enable = true; 87 - 88 - # Only create the nixbuild user (and its group) on build machines 89 - users.users.nixbuild = lib.mkIf isBuildMachine { 90 - uid = 1999; 91 - isNormalUser = true; 92 - createHome = false; 93 - group = "nixbuild"; 94 - 95 - openssh.authorizedKeys.keyFiles = config.myNixOS.sshKeyFiles.aly ++ config.myNixOS.sshKeyFiles.root; 96 - }; 97 - 98 - users.groups.nixbuild = lib.mkIf isBuildMachine {}; 99 - 100 - myNixOS.programs.njust.recipes.nix = '' 101 - # Garbage collect Nix store 102 - [group('nix')] 103 - gc-nix days="3": 104 - @echo "Cleaning up Nix generations older than {{days}} days..." 105 - sudo nix-collect-garbage --delete-older-than {{days}}d 106 - 107 - # Optimize Nix store 108 - [group('nix')] 109 - optimize-nix: 110 - @echo "Optimizing Nix store..." 111 - sudo nix-store --optimise 112 - 113 - # Free space from Nix store 114 - [group('nix')] 115 - cleanup-nix: gc-nix && optimize-nix 116 - 117 - # Repair Nix store 118 - [group('nix')] 119 - repair-nix: 120 - sudo nix-store --repair --verify --check-contents 121 - ''; 122 - }; 123 - }
-148
nix/modules/nixos/programs/njust.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: let 7 - cfg = config.myNixOS.programs.njust; 8 - 9 - # Merge all recipe files into a single justfile 10 - mergedJustfileContent = '' 11 - _default: 12 - @printf '\033[1;36mnjust\033[0m\n' 13 - @printf 'Just-based recipe runner for NixOS.\n\n' 14 - @printf '\033[1;33mUsage:\033[0m njust <recipe> [args...]\n\n' 15 - @njust --list --list-heading $'Available recipes:\n\n' 16 - 17 - ${lib.concatStringsSep "\n" (lib.attrValues cfg.recipes)} 18 - ''; 19 - 20 - # Validate the justfile syntax 21 - validatedJustfile = 22 - pkgs.runCommand "njust-justfile-validated" { 23 - nativeBuildInputs = [pkgs.just]; 24 - preferLocalBuild = true; 25 - } '' 26 - # Write the justfile content to a temporary file 27 - echo ${lib.escapeShellArg mergedJustfileContent} > justfile 28 - 29 - # Validate the justfile syntax 30 - echo "Validating njust justfile syntax..." 31 - just --justfile justfile --summary >/dev/null || { 32 - echo "ERROR: njust justfile has syntax errors!" 33 - echo "Justfile content:" 34 - cat justfile 35 - exit 1 36 - } 37 - 38 - # Copy validated justfile to output 39 - cp justfile $out 40 - echo "njust justfile validation passed" 41 - ''; 42 - 43 - mergedJustfile = validatedJustfile; 44 - 45 - njustScript = pkgs.writeShellApplication { 46 - name = "njust"; 47 - runtimeInputs = [pkgs.jq pkgs.just]; 48 - 49 - text = '' 50 - exec just --working-directory "$PWD" --justfile ${mergedJustfile} "$@" 51 - ''; 52 - }; 53 - in { 54 - options.myNixOS.programs.njust = { 55 - enable = lib.mkEnableOption "njust helper"; 56 - 57 - recipes = lib.mkOption { 58 - type = lib.types.attrsOf lib.types.str; 59 - default = {}; 60 - 61 - description = '' 62 - Attribute set of recipe names to justfile content. 63 - Each recipe will be merged into the final justfile. 64 - ''; 65 - }; 66 - 67 - defaultRecipes = lib.mkOption { 68 - type = lib.types.bool; 69 - default = true; 70 - description = "Whether to include default system management recipes"; 71 - }; 72 - }; 73 - 74 - config = lib.mkIf cfg.enable { 75 - myNixOS.programs.njust.recipes = lib.mkIf cfg.defaultRecipes { 76 - system = '' 77 - # Show system info 78 - [group('system')] 79 - info: 80 - @echo "Hostname: $(hostname)" 81 - @echo "NixOS Version: $(nixos-version)" 82 - @echo "Kernel: $(uname -r)" 83 - @echo "Generation: $(sudo nix-env --list-generations -p /nix/var/nix/profiles/system | tail -1 | awk '{print $1}')" 84 - @echo "Revision: $(nixos-version --json | jq -r '.configurationRevision // "unknown"')" 85 - ''; 86 - 87 - updates = '' 88 - # Update everything 89 - [group('system')] 90 - update: update-nixos update-nix-profile 91 - 92 - # Update NixOS system 93 - [group('nix')] 94 - update-nixos action="switch": 95 - @echo "Updating NixOS..." 96 - sudo nixos-rebuild {{action}} --flake "${config.myNixOS.FLAKE}" 97 - 98 - # Update Nix user profile 99 - [group('nix')] 100 - update-nix-profile: 101 - @echo "Updating Nix user profile..." 102 - nix profile upgrade --all 103 - ''; 104 - 105 - secureboot = '' 106 - # Check Secure Boot status 107 - [group('secureboot')] 108 - sb-status: 109 - sudo bootctl status 110 - 111 - # Generate Secure Boot keys 112 - [group('secureboot')] 113 - gen-sb-keys: 114 - @echo "Generating Secure Boot keys..." 115 - sudo sbctl create-keys 116 - 117 - # Enroll Secure Boot keys 118 - [group('secureboot')] 119 - enroll-sb-keys: 120 - @echo "Enrolling Secure Boot keys..." 121 - sudo sbctl enroll-keys --microsoft 122 - ''; 123 - 124 - full-disk-encryption = '' 125 - # List encrypted volumes 126 - [group('encryption')] 127 - fde-status: 128 - @echo "Checking Full Disk Encryption status..." 129 - lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,TYPE,UUID | grep crypt 130 - 131 - # Enable TPM2 disk unlock 132 - [group('encryption')] 133 - [confirm("Verify ecure Boot is active before continuing!")] 134 - enable-tpm2-unlock crypt="/dev/nvme0n1p2": 135 - @echo "Setting up TPM2 disk unlocking for {{crypt}}..." 136 - sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+2+7+12 --wipe-slot=tpm2 {{crypt}} 137 - 138 - # Enable FIDO2 disk unlock 139 - [group('encryption')] 140 - enable-fido2-unlock crypt="/dev/nvme0n1p2": 141 - @echo "Setting up FIDO2 disk unlocking for {{crypt}}..." 142 - sudo systemd-cryptenroll --fido2-device=auto {{crypt}} 143 - ''; 144 - }; 145 - 146 - environment.systemPackages = [njustScript]; 147 - }; 148 - }
-23
nix/modules/nixos/programs/podman.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: { 7 - options.myNixOS.programs.podman.enable = lib.mkEnableOption "podman container runtime"; 8 - 9 - config = lib.mkIf config.myNixOS.programs.podman.enable { 10 - environment.systemPackages = lib.optionals config.services.xserver.enable [pkgs.pods]; 11 - 12 - virtualisation = { 13 - oci-containers = {backend = "podman";}; 14 - 15 - podman = { 16 - enable = true; 17 - autoPrune.enable = true; 18 - defaultNetwork.settings.dns_enabled = true; # Required for containers under podman-compose to be able to talk to each other. 19 - dockerCompat = true; 20 - }; 21 - }; 22 - }; 23 - }
-22
nix/modules/nixos/programs/systemd-boot.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.programs.systemd-boot.enable = lib.mkEnableOption "boot with systemd-boot"; 7 - 8 - config = lib.mkIf config.myNixOS.programs.systemd-boot.enable { 9 - boot = { 10 - initrd.systemd.enable = lib.mkDefault true; 11 - 12 - loader = { 13 - efi.canTouchEfiVariables = lib.mkDefault true; 14 - 15 - systemd-boot = { 16 - enable = lib.mkDefault true; 17 - configurationLimit = lib.mkDefault 10; 18 - }; 19 - }; 20 - }; 21 - }; 22 - }
+6 -8
nix/modules/nixos/programs/virt-manager.nix nix/nixos/programs/virt-manager.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.programs.virt-manager.enable = lib.mkEnableOption "virt-manager virtualization manager"; 7 - 8 - config = lib.mkIf config.myNixOS.programs.virt-manager.enable { 1 + _: { 2 + flake.modules.nixos.virt-manager = { 3 + config, 4 + lib, 5 + ... 6 + }: { 9 7 programs = { 10 8 dconf.profiles.user.databases = lib.optionals config.services.xserver.enable [ 11 9 {
-51
nix/modules/nixos/services/alloy.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.alloy = { 7 - enable = lib.mkEnableOption "grafana alloy for tailing logs"; 8 - 9 - lokiUrl = lib.mkOption { 10 - description = "Loki URL to report to"; 11 - default = "https://loki.narwhal-snapper.ts.net/loki/api/v1/push"; 12 - type = lib.types.str; 13 - }; 14 - }; 15 - 16 - config = lib.mkIf config.myNixOS.services.alloy.enable { 17 - services.alloy.enable = true; 18 - 19 - environment.etc."alloy/config.alloy".text = '' 20 - loki.write "default" { 21 - endpoint { 22 - url = "${config.myNixOS.services.alloy.lokiUrl}" 23 - } 24 - } 25 - 26 - loki.relabel "journal" { 27 - forward_to = [] 28 - 29 - rule { 30 - source_labels = ["__journal__systemd_unit"] 31 - target_label = "unit" 32 - } 33 - 34 - rule { 35 - source_labels = ["__journal__systemd_user_unit"] 36 - target_label = "user_unit" 37 - } 38 - } 39 - 40 - loki.source.journal "read" { 41 - forward_to = [loki.write.default.receiver] 42 - relabel_rules = loki.relabel.journal.rules 43 - max_age = "12h" 44 - labels = { 45 - job = "systemd-journal", 46 - host = "${config.networking.hostName}", 47 - } 48 - } 49 - ''; 50 - }; 51 - }
-49
nix/modules/nixos/services/atbbs.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.atbbs = { 7 - enable = lib.mkEnableOption "atproto bbs"; 8 - 9 - port = lib.mkOption { 10 - description = "Port to listen on."; 11 - default = 8582; 12 - type = lib.types.int; 13 - }; 14 - 15 - telnetPort = lib.mkOption { 16 - description = "Port to listen on for telnet."; 17 - default = 2323; 18 - type = lib.types.int; 19 - }; 20 - }; 21 - 22 - config = lib.mkIf config.myNixOS.services.atbbs.enable { 23 - networking.firewall.allowedTCPPorts = [ 24 - config.myNixOS.services.atbbs.port 25 - config.myNixOS.services.atbbs.telnetPort 26 - ]; 27 - 28 - virtualisation.oci-containers = { 29 - backend = "podman"; 30 - 31 - containers = { 32 - atbbs = { 33 - extraOptions = ["--pull=always"]; 34 - image = "ghcr.io/alyraffauf/atbbs"; 35 - environment.PUBLIC_URL = "https://atbbs.xyz"; 36 - ports = ["0.0.0.0:${toString config.myNixOS.services.atbbs.port}:80"]; 37 - }; 38 - 39 - atbbs-telnet = { 40 - extraOptions = ["--pull=always"]; 41 - image = "ghcr.io/alyraffauf/atbbs-telnet"; 42 - ports = ["0.0.0.0:${toString config.myNixOS.services.atbbs.telnetPort}:2323"]; 43 - }; 44 - }; 45 - }; 46 - 47 - myNixOS.programs.podman.enable = true; 48 - }; 49 - }
-19
nix/modules/nixos/services/cachefilesd.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.cachefilesd.enable = lib.mkEnableOption "cachefilesd for nfs and smb caching"; 7 - 8 - config = lib.mkIf config.myNixOS.services.cachefilesd.enable { 9 - services.cachefilesd = { 10 - enable = true; 11 - 12 - extraConfig = '' 13 - brun 20% 14 - bcull 10% 15 - bstop 5% 16 - ''; 17 - }; 18 - }; 19 - }
+7 -10
nix/modules/nixos/services/caddy.nix nix/nixos/services/caddy.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: { 8 - options.myNixOS.services.caddy.enable = lib.mkEnableOption "Caddy web server."; 9 - 10 - config = lib.mkIf config.myNixOS.services.caddy.enable { 1 + {self, ...}: { 2 + flake.modules.nixos.caddy = { 3 + config, 4 + pkgs, 5 + ... 6 + }: { 11 7 sops.secrets.tailscaleCaddyAuth = { 12 8 sopsFile = "${self}/secrets/tailscale.yaml"; 13 9 key = "caddy_auth_env"; 14 10 }; 11 + 15 12 networking.firewall.allowedTCPPorts = [80 443]; 16 13 17 14 services = {
+2 -9
nix/modules/nixos/services/fail2ban.nix nix/nixos/services/fail2ban.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.fail2ban.enable = lib.mkEnableOption "fail2ban"; 7 - 8 - config = lib.mkIf config.myNixOS.services.fail2ban.enable { 1 + _: { 2 + flake.modules.nixos.fail2ban = {config, ...}: { 9 3 environment.etc = { 10 4 "fail2ban/filter.d/audiobookshelf.conf".text = '' 11 5 [Definition] ··· 121 115 }; 122 116 }; 123 117 124 - # Generic scanner / bot patterns (wp-login.php, sqladmin, etc.) 125 118 nginx-botsearch = { 126 119 settings = { 127 120 enabled = true;
-91
nix/modules/nixos/services/forgejo-runner.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - self, 6 - ... 7 - }: { 8 - options.myNixOS.services.forgejo-runner = { 9 - enable = lib.mkEnableOption "Forĝejo runners"; 10 - 11 - nativeRunners = lib.mkOption { 12 - type = lib.types.int; 13 - default = 1; 14 - description = "How many native NixOS runners to run for the Forĝejo runner."; 15 - }; 16 - 17 - dockerContainers = lib.mkOption { 18 - type = lib.types.int; 19 - default = 1; 20 - description = "How many docker containers to run for the Forĝejo runner."; 21 - }; 22 - }; 23 - 24 - config = lib.mkIf config.myNixOS.services.forgejo-runner.enable { 25 - assertions = [ 26 - { 27 - assertion = config.services.tailscale.enable; 28 - message = "We contact Forĝejo over tailscale, but services.tailscale.enable != true."; 29 - } 30 - ]; 31 - 32 - sops.secrets.act-runner = { 33 - sopsFile = "${self}/secrets/act-runner.yaml"; 34 - key = "TOKEN"; 35 - }; 36 - 37 - services.gitea-actions-runner = let 38 - arch = lib.replaceStrings ["-"] ["_"] pkgs.stdenv.hostPlatform.system; 39 - in { 40 - instances = let 41 - tokenFile = config.sops.secrets.act-runner.path; 42 - in { 43 - alycodes-containers = { 44 - inherit tokenFile; 45 - enable = true; 46 - 47 - labels = lib.optional (arch == "aarch64_linux") "ubuntu-24.04-arm:docker://gitea/runner-images:ubuntu-latest" ++ lib.optional (arch == "x86_64_linux") "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"; 48 - 49 - name = "${arch}-${config.networking.hostName}-alycodes-containers"; 50 - 51 - settings = { 52 - container.network = "host"; 53 - runner.capacity = config.myNixOS.services.forgejo-runner.dockerContainers; 54 - }; 55 - 56 - url = "https://git.aly.codes"; 57 - }; 58 - 59 - alycodes-nixos = { 60 - inherit tokenFile; 61 - enable = true; 62 - 63 - hostPackages = with pkgs; 64 - [ 65 - bash 66 - cachix 67 - coreutils 68 - curl 69 - gawk 70 - gitMinimal 71 - gnused 72 - jq 73 - nodejs 74 - wget 75 - ] 76 - ++ [config.nix.package]; 77 - 78 - labels = ["nixos-${arch}:host"]; 79 - name = "${arch}-${config.networking.hostName}-alycodes-nixos"; 80 - 81 - settings = { 82 - container.network = "host"; 83 - runner.capacity = config.myNixOS.services.forgejo-runner.nativeRunners; 84 - }; 85 - 86 - url = "https://git.aly.codes"; 87 - }; 88 - }; 89 - }; 90 - }; 91 - }
-59
nix/modules/nixos/services/plex.nix
··· 1 - { 2 - config, 3 - lib, 4 - self, 5 - ... 6 - }: { 7 - options.myNixOS.services.plex = { 8 - enable = lib.mkEnableOption "plex media server"; 9 - 10 - dataDir = lib.mkOption { 11 - description = "Data directory to use."; 12 - default = "/var/lib"; 13 - type = lib.types.str; 14 - }; 15 - 16 - tautulli.enable = lib.mkEnableOption "tautulli"; 17 - }; 18 - 19 - config = lib.mkMerge [ 20 - (lib.mkIf config.myNixOS.services.plex.enable { 21 - services.plex = { 22 - enable = true; 23 - dataDir = "${config.myNixOS.services.plex.dataDir}/plex"; 24 - 25 - extraPlugins = [ 26 - (builtins.path { 27 - name = "Audnexus.bundle"; 28 - path = self.inputs.audnexus; 29 - }) 30 - (builtins.path { 31 - name = "Hama.bundle"; 32 - path = self.inputs.hama; 33 - }) 34 - ]; 35 - 36 - extraScanners = [ 37 - (builtins.path { 38 - name = "Absolute-Series-Scanner"; 39 - path = self.inputs.absolute; 40 - }) 41 - ]; 42 - 43 - openFirewall = true; 44 - }; 45 - 46 - # Plex's main process exits on SIGTERM but leaves transcoder children 47 - # alive in the cgroup, so systemd waits the full default 90s before 48 - # SIGKILL on every shutdown. Cap to 15s — the cache regenerates. 49 - systemd.services.plex.serviceConfig.TimeoutStopSec = 15; 50 - }) 51 - 52 - (lib.mkIf config.myNixOS.services.plex.tautulli.enable { 53 - services.tautulli = { 54 - enable = true; 55 - openFirewall = true; 56 - }; 57 - }) 58 - ]; 59 - }
-25
nix/modules/nixos/services/prometheusNode.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.prometheusNode = { 7 - enable = lib.mkEnableOption "prometheus node exporter for monitoring system metrics"; 8 - }; 9 - 10 - config = lib.mkIf config.myNixOS.services.prometheusNode.enable { 11 - services.prometheus.exporters.node = { 12 - enable = true; 13 - enabledCollectors = ["systemd"]; 14 - 15 - extraFlags = [ 16 - "--collector.ethtool" 17 - "--collector.softirqs" 18 - "--collector.tcpstat" 19 - "--collector.wifi" 20 - ]; 21 - 22 - port = 3021; 23 - }; 24 - }; 25 - }
-65
nix/modules/nixos/services/qbittorrent.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: let 7 - cfg = config.myNixOS.services.qbittorrent; 8 - in { 9 - options.myNixOS.services.qbittorrent = { 10 - enable = lib.mkEnableOption "qBittorrent headless"; 11 - 12 - dataDir = lib.mkOption { 13 - type = lib.types.path; 14 - default = "/var/lib/qbittorrent"; 15 - description = "The directory where qBittorrent stores its data files."; 16 - }; 17 - 18 - user = lib.mkOption { 19 - type = lib.types.str; 20 - default = "qbittorrent"; 21 - description = "User account under which qBittorrent runs."; 22 - }; 23 - 24 - group = lib.mkOption { 25 - type = lib.types.str; 26 - default = "qbittorrent"; 27 - description = "Group under which qBittorrent runs."; 28 - }; 29 - 30 - port = lib.mkOption { 31 - type = lib.types.port; 32 - default = 8080; 33 - description = "qbittorrent web UI port."; 34 - }; 35 - 36 - openFirewall = lib.mkOption { 37 - type = lib.types.bool; 38 - default = false; 39 - description = "qbittorrent.port to the outside network."; 40 - }; 41 - 42 - package = lib.mkOption { 43 - type = lib.types.package; 44 - default = pkgs.qbittorrent-nox; 45 - defaultText = lib.literalExpression "pkgs.qbittorrent-nox"; 46 - description = "The qbittorrent package to use."; 47 - }; 48 - }; 49 - 50 - config = lib.mkIf cfg.enable { 51 - services.qbittorrent = { 52 - inherit 53 - (config.myNixOS.services.qbittorrent) 54 - openFirewall 55 - user 56 - group 57 - package 58 - ; 59 - 60 - enable = true; 61 - profileDir = cfg.dataDir; 62 - webuiPort = cfg.port; 63 - }; 64 - }; 65 - }
-173
nix/modules/nixos/services/syncthing.nix
··· 1 - { 2 - config, 3 - lib, 4 - ... 5 - }: { 6 - options.myNixOS.services.syncthing = { 7 - enable = lib.mkEnableOption "Syncthing file syncing service."; 8 - 9 - certFile = lib.mkOption { 10 - description = "Path to the certificate file."; 11 - type = lib.types.path; 12 - }; 13 - 14 - keyFile = lib.mkOption { 15 - description = "Path to the key file."; 16 - type = lib.types.path; 17 - }; 18 - 19 - romsPath = lib.mkOption { 20 - default = "/home/${config.myNixOS.services.syncthing.user}/roms"; 21 - description = "Path to the ROM folder."; 22 - type = lib.types.path; 23 - }; 24 - 25 - syncROMs = lib.mkEnableOption "Whether to sync ROMs."; 26 - 27 - user = lib.mkOption { 28 - description = "User to run Syncthing as."; 29 - type = lib.types.str; 30 - }; 31 - }; 32 - 33 - config = lib.mkIf config.myNixOS.services.syncthing.enable { 34 - systemd.services.syncthing.environment.STNODEFAULTFOLDER = "true"; 35 - 36 - services = { 37 - caddy.virtualHosts = 38 - lib.mkIf 39 - ( 40 - config.myNixOS.services.caddy.enable 41 - && config.myNixOS.services.tailscale.enable 42 - ) { 43 - "syncthing-${config.networking.hostName}.narwhal-snapper.ts.net" = { 44 - extraConfig = '' 45 - bind tailscale/syncthing-${config.networking.hostName} 46 - reverse_proxy localhost:8384 { 47 - header_up Host localhost 48 - } 49 - ''; 50 - }; 51 - }; 52 - 53 - syncthing = let 54 - cfg = config.myNixOS.services.syncthing; 55 - 56 - devices = { 57 - "allyx" = {id = "XTEVJX2-DBEFN4X-UCG43YR-V4FOQYU-DMM2WH4-AMCC5FS-42UB3DM-KUDVHQL";}; 58 - "eterna" = {id = "ZAD2MVO-I2OQII4-C3T756B-BEBQMM6-Q4ILH2H-5CR3TMI-DR4VBFD-GLRVOQK";}; 59 - "fallarbor" = {id = "P4URLH4-YWLMO6J-W62ET7H-TQAO3Y6-T2FAYOY-C2VTI65-VQXHVGG-NQ76PAZ";}; 60 - "fortree" = {id = "S6PVA3I-EKOCGIU-GFX7AE6-FXM45OW-JTYN5LJ-UZ4LADZ-NNAJGDD-KST2VAG";}; 61 - "groudon" = {id = "VOEAEAG-NP5Z3BM-DK5FO75-6G4NKSJ-3EUNFSV-VIR4KDH-OM6ZN7L-OOQKCQJ";}; 62 - "jubilife" = {id = "52MTCMC-PKEWSAU-HADMTZU-DY5EKFO-B323P7V-OBXLNTQ-EJY7F7Y-EUWFBQX";}; 63 - "kyogre" = {id = "SBQNUXS-H4XDJ3E-RBHJPT5-45WDJJA-2U43M4P-23XGUJ7-E3CNNKZ-BXSGIA3";}; 64 - "oreburgh" = {id = "RFVF6DA-CQJLXTP-RKMYEB3-D2KMWJH-3Z2CIAN-PNYOXI6-FIDBFWG-JJA57AX";}; 65 - "pacifidlog" = {id = "6EBVXYI-HZW4LQI-T6L3TTI-DZEBXJM-RP3DW7N-BCAG6FC-G2654DN-XJFSLQD";}; 66 - "petalburg" = {id = "O75EK2H-YBXPM5D-PBYV7XB-DJKFL3E-OFZBB7H-MLCD2UT-NXQRMDG-BTZZQQH";}; 67 - "rp5" = {id = "5AKCXRS-XU7BBSS-RDPSSLG-4BDOZ4K-OXLFQZX-HJSM53W-4GLSOAC-RZ7APA7";}; 68 - "rpclassic" = {id = "EDNFUWI-UFYEOPI-QPJIEWF-NXVEGZ6-2J7IXW7-X22L27F-VU6JKSB-CH6GDAO";}; 69 - "rustboro" = {id = "NY53BFH-CPVCXGH-MI5AT7E-WBK7TXS-5NQDSCW-J5BALLV-EGS2VJL-CMED2AH";}; 70 - "slateport" = {id = "MDJFDUG-UJAXQXI-AMEF2AR-PBMD5QK-Z5ZG6AA-RCJCU3M-GZHQQEA-X2JGOAK";}; 71 - "snowpoint" = {id = "TFSZWZB-EDIFV2P-333APP2-T655TM4-2XGA7QA-P22Z36W-3RNGX2C-DLETAQ7";}; 72 - "sootopolis" = {id = "7QGSZ2D-CGMLPWD-OCFCBXP-7746W7F-COFV52F-Q2PMCAS-GV5DCSV-6NIXDQJ";}; 73 - "thor" = {id = "B33X5WA-S6P4XEE-VURE4PB-WKMXHLP-6AG55LZ-QIG6ZJG-GDGXWAD-EDVIRAF";}; 74 - "verdanturf" = {id = "CQ7A2KW-2JRZHEO-NF6NZLY-C2OX4SO-EPKQRMV-7YBSSFA-FJ2CW2P-NOIKPQB";}; 75 - }; 76 - 77 - folders = lib.mkMerge [ 78 - { 79 - "sync" = { 80 - devices = [ 81 - "allyx" 82 - "eterna" 83 - "fallarbor" 84 - "fortree" 85 - "groudon" 86 - "jubilife" 87 - "kyogre" 88 - "oreburgh" 89 - "pacifidlog" 90 - "petalburg" 91 - "rustboro" 92 - "slateport" 93 - "snowpoint" 94 - "sootopolis" 95 - "verdanturf" 96 - ]; 97 - id = "default"; 98 - path = "~/sync"; 99 - versioning = { 100 - params.cleanoutDays = "5"; 101 - type = "trashcan"; 102 - }; 103 - }; 104 - 105 - "screenshots" = { 106 - devices = [ 107 - "fallarbor" 108 - "oreburgh" 109 - "jubilife" 110 - "pacifidlog" 111 - "petalburg" 112 - "rustboro" 113 - "slateport" 114 - "snowpoint" 115 - "sootopolis" 116 - "verdanturf" 117 - ]; 118 - id = "screenshots"; 119 - path = "~/pics/screenshots"; 120 - versioning = { 121 - params.cleanoutDays = "5"; 122 - type = "trashcan"; 123 - }; 124 - }; 125 - 126 - "roms" = { 127 - devices = [ 128 - "jubilife" 129 - "oreburgh" 130 - "pacifidlog" 131 - "petalburg" 132 - "rp5" 133 - "rpclassic" 134 - "rustboro" 135 - "sootopolis" 136 - "thor" 137 - ]; 138 - id = "emudeck"; 139 - versioning = { 140 - params.cleanoutDays = "3"; 141 - type = "trashcan"; 142 - }; 143 - }; 144 - } 145 - { 146 - "roms" = { 147 - enable = cfg.syncROMs; 148 - path = cfg.romsPath; 149 - }; 150 - } 151 - ]; 152 - in { 153 - enable = true; 154 - cert = cfg.certFile; 155 - configDir = "${config.services.syncthing.dataDir}/.syncthing"; 156 - dataDir = "/home/${cfg.user}"; 157 - key = cfg.keyFile; 158 - openDefaultPorts = true; 159 - inherit (cfg) user; 160 - 161 - settings = { 162 - options = { 163 - localAnnounceEnabled = true; 164 - relaysEnabled = true; 165 - urAccepted = -1; 166 - }; 167 - 168 - inherit devices folders; 169 - }; 170 - }; 171 - }; 172 - }; 173 - }
-98
nix/modules/nixos/services/tailscale.nix
··· 1 - { 2 - config, 3 - lib, 4 - self, 5 - ... 6 - }: { 7 - options.myNixOS.services.tailscale = { 8 - enable = lib.mkEnableOption "Tailscale VPN service"; 9 - 10 - authKeyFile = lib.mkOption { 11 - description = "Key file to use for authentication"; 12 - default = config.sops.secrets.tailscaleAuthKey.path or null; 13 - type = lib.types.nullOr lib.types.path; 14 - }; 15 - 16 - caddy.enable = lib.mkEnableOption "serving supported local services on Tailnet with Caddy"; 17 - 18 - operator = lib.mkOption { 19 - description = "Tailscale operator name"; 20 - default = null; 21 - type = lib.types.nullOr lib.types.str; 22 - }; 23 - }; 24 - 25 - config = lib.mkIf config.myNixOS.services.tailscale.enable { 26 - sops.secrets.tailscaleAuthKey = { 27 - sopsFile = "${self}/secrets/tailscale.yaml"; 28 - key = "auth_key"; 29 - }; 30 - 31 - assertions = [ 32 - { 33 - assertion = config.myNixOS.services.tailscale.authKeyFile != null; 34 - message = "config.tailscale.authKeyFile cannot be null."; 35 - } 36 - ]; 37 - 38 - networking.firewall = { 39 - allowedUDPPorts = [config.services.tailscale.port]; 40 - trustedInterfaces = [config.services.tailscale.interfaceName]; 41 - }; 42 - 43 - services = { 44 - caddy = lib.mkIf config.myNixOS.services.tailscale.caddy.enable { 45 - enable = true; 46 - 47 - virtualHosts = { 48 - "${config.networking.hostName}.narwhal-snapper.ts.net".extraConfig = let 49 - syncthing = '' 50 - redir /syncthing /syncthing/ 51 - handle_path /syncthing/* { 52 - reverse_proxy localhost:8384 { 53 - header_up Host localhost 54 - } 55 - } 56 - ''; 57 - in 58 - lib.concatLines ( 59 - lib.optional config.services.syncthing.enable syncthing 60 - ); 61 - }; 62 - }; 63 - 64 - tailscale = { 65 - enable = true; 66 - inherit (config.myNixOS.services.tailscale) authKeyFile; 67 - 68 - extraUpFlags = 69 - ["--ssh"] 70 - ++ lib.optional (config.myNixOS.services.tailscale.operator != null) 71 - "--operator ${config.myNixOS.services.tailscale.operator}"; 72 - 73 - openFirewall = true; 74 - permitCertUid = lib.mkIf config.services.caddy.enable "caddy"; 75 - useRoutingFeatures = "both"; 76 - }; 77 - }; 78 - 79 - myNixOS.programs.njust.recipes.tailscale = '' 80 - # Connect to Mullvad NYC 81 - [group('tailscale')] 82 - enable-mullvad: 83 - @echo "Connecting to Mullvad NYC exit node..." 84 - tailscale set --exit-node=us-nyc-wg-301.mullvad.ts.net 85 - 86 - # Disconnect from Mullvad NYC 87 - [group('tailscale')] 88 - disable-mullvad: 89 - @echo "Disconnecting from exit node..." 90 - tailscale set --exit-node= 91 - 92 - # Show Tailscale status 93 - [group('tailscale')] 94 - ts-status: 95 - tailscale status 96 - ''; 97 - }; 98 - }
+60
nix/nixos/base.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.base = { 3 + config, 4 + pkgs, 5 + ... 6 + }: { 7 + environment = { 8 + etc."nixos".source = self; 9 + 10 + systemPackages = with pkgs; [ 11 + (inxi.override {withRecommends = true;}) 12 + helix 13 + lm_sensors 14 + python314 15 + rclone 16 + wget 17 + zellij 18 + ]; 19 + 20 + variables = { 21 + FLAKE = config.myFlakeUrl; 22 + NH_FLAKE = config.myFlakeUrl; 23 + }; 24 + }; 25 + 26 + programs = { 27 + direnv = { 28 + enable = true; 29 + nix-direnv.enable = true; 30 + silent = true; 31 + }; 32 + 33 + git.enable = true; 34 + htop.enable = true; 35 + nh.enable = true; 36 + }; 37 + 38 + networking.networkmanager.enable = true; 39 + security.sudo-rs.enable = true; 40 + 41 + services = { 42 + openssh = { 43 + enable = true; 44 + openFirewall = true; 45 + settings.PasswordAuthentication = false; 46 + }; 47 + 48 + timesyncd.enable = true; 49 + }; 50 + 51 + sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"]; 52 + 53 + system.configurationRevision = self.rev or self.dirtyRev or null; 54 + 55 + systemd = { 56 + coredump.enable = false; 57 + enableEmergencyMode = false; 58 + }; 59 + }; 60 + }
+9
nix/nixos/flake-url.nix
··· 1 + _: { 2 + flake.modules.nixos.flake-url = {lib, ...}: { 3 + options.myFlakeUrl = lib.mkOption { 4 + type = lib.types.str; 5 + default = "github:alyraffauf/cute.haus"; 6 + description = "Default flake URL for this NixOS configuration."; 7 + }; 8 + }; 9 + }
+63
nix/nixos/known-hosts.nix
··· 1 + {self, ...}: let 2 + tnet = "narwhal-snapper.ts.net"; 3 + pub = host: "${self}/keys/root_${host}.pub"; 4 + in { 5 + flake.modules.nixos.known-hosts = { 6 + programs.ssh.knownHosts = { 7 + snowpoint = { 8 + hostNames = ["snowpoint" "snowpoint.local" "snowpoint.${tnet}" "dewford" "dewford.local" "dewford.${tnet}"]; 9 + publicKeyFile = pub "snowpoint"; 10 + }; 11 + 12 + fallarbor = { 13 + hostNames = ["fallarbor" "fallarbor.local" "fallarbor.${tnet}"]; 14 + publicKeyFile = pub "fallarbor"; 15 + }; 16 + 17 + fortree = { 18 + hostNames = ["fortree" "fortree.local" "fortree.${tnet}"]; 19 + publicKeyFile = pub "fortree"; 20 + }; 21 + 22 + jubilife = { 23 + hostNames = ["jubilife" "jubilife.local" "jubilife.${tnet}" "lilycove" "lilycove.local" "lilycove.${tnet}"]; 24 + publicKeyFile = pub "jubilife"; 25 + }; 26 + 27 + pastoria = { 28 + hostNames = ["pastoria" "pastoria.local" "pastoria.${tnet}"]; 29 + publicKeyFile = pub "pastoria"; 30 + }; 31 + 32 + eterna = { 33 + hostNames = ["eterna" "eterna.local" "eterna.${tnet}" "mauville" "mauville.local" "mauville.${tnet}"]; 34 + publicKeyFile = pub "eterna"; 35 + }; 36 + 37 + solaceon = { 38 + hostNames = ["solaceon" "solaceon.local" "solaceon.${tnet}" "mossdeep" "mossdeep.local" "mossdeep.${tnet}"]; 39 + publicKeyFile = pub "solaceon"; 40 + }; 41 + 42 + petalburg = { 43 + hostNames = ["petalburg" "petalburg.local" "petalburg.${tnet}"]; 44 + publicKeyFile = pub "petalburg"; 45 + }; 46 + 47 + rustboro = { 48 + hostNames = ["rustboro" "rustboro.local" "rustboro.${tnet}"]; 49 + publicKeyFile = pub "rustboro"; 50 + }; 51 + 52 + slateport = { 53 + hostNames = ["slateport" "slateport.local" "slateport.${tnet}"]; 54 + publicKeyFile = pub "slateport"; 55 + }; 56 + 57 + sootopolis = { 58 + hostNames = ["sootopolis" "sootopolis.local" "sootopolis.${tnet}"]; 59 + publicKeyFile = pub "sootopolis"; 60 + }; 61 + }; 62 + }; 63 + }
+94
nix/nixos/profiles/arr.nix
··· 1 + _: { 2 + flake.modules.nixos.arr = { 3 + config, 4 + lib, 5 + options, 6 + pkgs, 7 + ... 8 + }: let 9 + stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 10 + start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 11 + in { 12 + options.myArr.dataDir = lib.mkOption { 13 + type = lib.types.str; 14 + default = "/var/lib"; 15 + description = "The directory where *arr stores its data files."; 16 + }; 17 + 18 + config = lib.mkMerge [ 19 + { 20 + services = { 21 + bazarr = { 22 + enable = true; 23 + dataDir = "${config.myArr.dataDir}/bazarr"; 24 + openFirewall = true; 25 + }; 26 + 27 + lidarr = { 28 + enable = true; 29 + dataDir = "${config.myArr.dataDir}/lidarr/.config/Lidarr"; 30 + openFirewall = true; 31 + }; 32 + 33 + prowlarr = { 34 + enable = true; 35 + openFirewall = true; 36 + }; 37 + 38 + radarr = { 39 + enable = true; 40 + dataDir = "${config.myArr.dataDir}/radarr/.config/Radarr/"; 41 + openFirewall = true; 42 + }; 43 + 44 + sonarr = { 45 + enable = true; 46 + dataDir = "${config.myArr.dataDir}/sonarr/.config/NzbDrone/"; 47 + openFirewall = true; 48 + }; 49 + }; 50 + 51 + systemd.tmpfiles.rules = [ 52 + "d ${config.services.lidarr.dataDir} 0755 lidarr lidarr" 53 + "d ${config.services.radarr.dataDir} 0755 radarr radarr" 54 + "d ${config.services.readarr.dataDir} 0755 readarr readarr" 55 + "d ${config.services.sonarr.dataDir} 0755 sonarr sonarr" 56 + ]; 57 + } 58 + 59 + (lib.optionalAttrs (options ? myBackups) { 60 + myBackups.jobs = { 61 + bazarr = { 62 + backupCleanupCommand = start "bazarr"; 63 + backupPrepareCommand = stop "bazarr"; 64 + paths = [config.services.bazarr.dataDir]; 65 + }; 66 + 67 + lidarr = { 68 + backupCleanupCommand = start "lidarr"; 69 + backupPrepareCommand = stop "lidarr"; 70 + paths = [config.services.lidarr.dataDir]; 71 + }; 72 + 73 + prowlarr = { 74 + backupCleanupCommand = start "prowlarr"; 75 + backupPrepareCommand = stop "prowlarr"; 76 + paths = [config.services.prowlarr.dataDir]; 77 + }; 78 + 79 + radarr = { 80 + backupCleanupCommand = start "radarr"; 81 + backupPrepareCommand = stop "radarr"; 82 + paths = [config.services.radarr.dataDir]; 83 + }; 84 + 85 + sonarr = { 86 + backupCleanupCommand = start "sonarr"; 87 + backupPrepareCommand = stop "sonarr"; 88 + paths = [config.services.sonarr.dataDir]; 89 + }; 90 + }; 91 + }) 92 + ]; 93 + }; 94 + }
+44
nix/nixos/profiles/autoUpgrade.nix
··· 1 + _: { 2 + flake.modules.nixos.auto-upgrade = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.myAutoUpgrade = { 8 + operation = lib.mkOption { 9 + type = lib.types.str; 10 + default = "switch"; 11 + description = "Operation to perform on auto-upgrade. Can be 'boot', 'switch', or 'test'."; 12 + }; 13 + 14 + dates = lib.mkOption { 15 + type = lib.types.str; 16 + default = "02:00"; 17 + description = "systemd OnCalendar expression for when the upgrade fires."; 18 + }; 19 + 20 + randomizedDelaySec = lib.mkOption { 21 + type = lib.types.str; 22 + default = "0"; 23 + description = "Random delay added on top of `dates`."; 24 + }; 25 + }; 26 + 27 + config = { 28 + system.autoUpgrade = { 29 + inherit (config.myAutoUpgrade) operation dates randomizedDelaySec; 30 + 31 + enable = true; 32 + allowReboot = true; 33 + flags = ["--accept-flake-config"]; 34 + flake = config.myFlakeUrl; 35 + persistent = true; 36 + 37 + rebootWindow = { 38 + lower = "02:00"; 39 + upper = "06:00"; 40 + }; 41 + }; 42 + }; 43 + }; 44 + }
+103
nix/nixos/profiles/b2-mounts.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.b2-mounts = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: let 8 + cfg = config.myB2Mounts; 9 + 10 + b2Options = [ 11 + "allow_other" 12 + "args2env" 13 + "cache-dir=${cfg.cacheDir}" 14 + "config=${config.sops.secrets.b2-mount-rclone.path}" 15 + "dir-cache-time=1h" 16 + "nodev" 17 + "nofail" 18 + "vfs-cache-mode=full" 19 + "vfs-write-back=10s" 20 + "x-systemd.after=network-online.target" 21 + "x-systemd.automount" 22 + ]; 23 + 24 + b2ProfileOptions = { 25 + audio = [ 26 + "buffer-size=128M" 27 + "vfs-cache-max-age=168h" 28 + "vfs-cache-max-size=${cfg.audioCacheSize}" 29 + "vfs-read-ahead=${cfg.audioReadAhead}" 30 + ]; 31 + 32 + video = [ 33 + "buffer-size=512M" 34 + "vfs-cache-max-age=336h" 35 + "vfs-cache-max-size=${cfg.videoCacheSize}" 36 + "vfs-read-ahead=${cfg.videoReadAhead}" 37 + ]; 38 + }; 39 + 40 + mkB2Mount = name: remote: profile: { 41 + "/mnt/Backblaze/${name}" = { 42 + device = "b2:${remote}"; 43 + fsType = "rclone"; 44 + options = b2Options ++ b2ProfileOptions.${profile}; 45 + }; 46 + }; 47 + 48 + allShares = { 49 + Anime = mkB2Mount "Anime" "aly-anime" "video"; 50 + Audiobooks = mkB2Mount "Audiobooks" "aly-audiobooks" "audio"; 51 + Movies = mkB2Mount "Movies" "aly-movies" "video"; 52 + Music = mkB2Mount "Music" "aly-music" "audio"; 53 + Shows = mkB2Mount "Shows" "aly-shows" "video"; 54 + }; 55 + in { 56 + options.myB2Mounts = { 57 + cacheDir = lib.mkOption { 58 + description = "Directory for rclone VFS cache."; 59 + example = "/mnt/Data/.rclone-cache"; 60 + type = lib.types.str; 61 + }; 62 + 63 + audioCacheSize = lib.mkOption { 64 + default = "15G"; 65 + type = lib.types.str; 66 + }; 67 + videoCacheSize = lib.mkOption { 68 + default = "50G"; 69 + type = lib.types.str; 70 + }; 71 + audioReadAhead = lib.mkOption { 72 + default = "1G"; 73 + type = lib.types.str; 74 + }; 75 + videoReadAhead = lib.mkOption { 76 + default = "3G"; 77 + type = lib.types.str; 78 + }; 79 + 80 + shares = lib.mkOption { 81 + description = "Which B2 shares to mount."; 82 + default = ["Anime" "Audiobooks" "Movies" "Music" "Shows"]; 83 + type = lib.types.listOf (lib.types.enum ["Anime" "Audiobooks" "Movies" "Music" "Shows"]); 84 + }; 85 + }; 86 + 87 + config = { 88 + sops.secrets.b2-mount-rclone = { 89 + sopsFile = "${self}/secrets/b2.yaml"; 90 + key = "rclone_config"; 91 + }; 92 + 93 + environment.systemPackages = [pkgs.rclone]; 94 + 95 + fileSystems = builtins.foldl' (a: b: a // b) {} (builtins.attrValues (builtins.intersectAttrs (builtins.listToAttrs (map (s: { 96 + name = s; 97 + value = null; 98 + }) 99 + cfg.shares)) 100 + allShares)); 101 + }; 102 + }; 103 + }
+84
nix/nixos/profiles/backups.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.backups = { 3 + config, 4 + lib, 5 + ... 6 + }: let 7 + backupDestination = "rclone:b2:aly-backups/${config.networking.hostName}"; 8 + mkRepo = service: "${backupDestination}/${service}"; 9 + restic = { 10 + extraBackupArgs = [ 11 + "--cleanup-cache" 12 + "--compression max" 13 + "--no-scan" 14 + ]; 15 + inhibitsSleep = true; 16 + initialize = true; 17 + passwordFile = config.sops.secrets.restic-passwd.path; 18 + pruneOpts = [ 19 + "--keep-daily 7" 20 + "--keep-weekly 4" 21 + "--keep-monthly 3" 22 + ]; 23 + rcloneConfigFile = config.sops.secrets.rclone-b2.path; 24 + timerConfig = { 25 + OnCalendar = "daily"; 26 + Persistent = true; 27 + RandomizedDelaySec = "3h"; 28 + }; 29 + }; 30 + in { 31 + options.myBackups.jobs = lib.mkOption { 32 + description = "Restic backup jobs rendered with the shared defaults."; 33 + default = {}; 34 + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { 35 + options = { 36 + paths = lib.mkOption { 37 + type = lib.types.listOf lib.types.path; 38 + description = "Paths to back up."; 39 + }; 40 + repository = lib.mkOption { 41 + type = lib.types.str; 42 + default = mkRepo name; 43 + description = "Restic repository URL."; 44 + }; 45 + backupPrepareCommand = lib.mkOption { 46 + type = lib.types.nullOr lib.types.str; 47 + default = null; 48 + }; 49 + backupCleanupCommand = lib.mkOption { 50 + type = lib.types.nullOr lib.types.str; 51 + default = null; 52 + }; 53 + exclude = lib.mkOption { 54 + type = lib.types.listOf lib.types.str; 55 + default = []; 56 + }; 57 + }; 58 + })); 59 + }; 60 + 61 + config = { 62 + sops.secrets = { 63 + restic-passwd = { 64 + sopsFile = "${self}/secrets/restic.yaml"; 65 + key = "PASSWORD"; 66 + }; 67 + 68 + rclone-b2 = { 69 + sopsFile = "${self}/secrets/b2.yaml"; 70 + key = "rclone_config"; 71 + }; 72 + }; 73 + 74 + services.restic.backups = lib.mapAttrs (_: job: 75 + restic 76 + // { 77 + inherit (job) paths repository exclude; 78 + backupPrepareCommand = lib.mkIf (job.backupPrepareCommand != null) job.backupPrepareCommand; 79 + backupCleanupCommand = lib.mkIf (job.backupCleanupCommand != null) job.backupCleanupCommand; 80 + }) 81 + config.myBackups.jobs; 82 + }; 83 + }; 84 + }
+92
nix/nixos/profiles/btrfs.nix
··· 1 + _: { 2 + flake.modules.nixos.btrfs = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: let 8 + btrfsFSDevices = let 9 + isDeviceInList = list: device: builtins.any (e: e.device == device) list; 10 + uniqueDeviceList = lib.foldl' (acc: e: 11 + if isDeviceInList acc e.device 12 + then acc 13 + else acc ++ [e]) []; 14 + in 15 + uniqueDeviceList ( 16 + lib.mapAttrsToList (_: fs: {inherit (fs) mountPoint device;}) 17 + (lib.filterAttrs (_: fs: fs.fsType == "btrfs") config.fileSystems) 18 + ); 19 + 20 + beesdConfig = lib.listToAttrs (map (fs: { 21 + name = lib.strings.sanitizeDerivationName (baseNameOf fs.device); 22 + value = { 23 + hashTableSizeMB = 2048; 24 + spec = fs.device; 25 + verbosity = "info"; 26 + extraOptions = ["--loadavg-target" "1.0" "--thread-factor" "0.50"]; 27 + }; 28 + }) 29 + btrfsFSDevices); 30 + 31 + hasHomeSubvolume = 32 + lib.hasAttr "/home" config.fileSystems 33 + && config.fileSystems."/home".fsType == "btrfs"; 34 + in { 35 + options.myBtrfs.deduplicate = lib.mkEnableOption "deduplicate btrfs filesystems"; 36 + 37 + config = lib.mkMerge [ 38 + { 39 + boot.supportedFilesystems = ["btrfs"]; 40 + environment.systemPackages = lib.optionals config.services.xserver.enable [pkgs.snapper-gui]; 41 + 42 + services = lib.mkIf (btrfsFSDevices != []) { 43 + beesd.filesystems = lib.mkIf config.myBtrfs.deduplicate beesdConfig; 44 + btrfs.autoScrub.enable = true; 45 + 46 + snapper = { 47 + configs.home = lib.mkIf hasHomeSubvolume { 48 + ALLOW_GROUPS = ["users"]; 49 + FSTYPE = "btrfs"; 50 + SUBVOLUME = "/home"; 51 + TIMELINE_CLEANUP = true; 52 + TIMELINE_CREATE = true; 53 + }; 54 + 55 + filters = '' 56 + -.bash_profile 57 + -.bashrc 58 + -.cache 59 + -.config 60 + -.librewolf 61 + -.local 62 + -.mozilla 63 + -.nix-profile 64 + -.pki 65 + -.share 66 + -.snapshots 67 + -.thunderbird 68 + -.zshrc 69 + ''; 70 + 71 + persistentTimer = true; 72 + }; 73 + }; 74 + } 75 + 76 + { 77 + myRecipes.btrfs = lib.mkIf (btrfsFSDevices != []) '' 78 + # List snapper snapshots 79 + [group('btrfs')] 80 + snapshots config="home": 81 + snapper -c {{config}} list 82 + 83 + # Create a manual snapshot 84 + [group('btrfs')] 85 + create-snapshot config="home" desc="manual": 86 + @echo "Creating snapshot '{{desc}}' for config {{config}}" 87 + snapper -c {{config}} create --description "{{desc}}" 88 + ''; 89 + } 90 + ]; 91 + }; 92 + }
+142
nix/nixos/profiles/k3s.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.k3s-node = { 3 + config, 4 + lib, 5 + options, 6 + pkgs, 7 + ... 8 + }: let 9 + cfg = config.myK3s; 10 + in { 11 + options.myK3s = { 12 + role = lib.mkOption { 13 + type = lib.types.enum ["server" "agent"]; 14 + default = "server"; 15 + }; 16 + 17 + clusterInit = lib.mkOption { 18 + type = lib.types.bool; 19 + default = false; 20 + description = '' 21 + Whether this node initializes the cluster's etcd. Exactly one node 22 + in the cluster should set this. Other servers join via `serverAddr`. 23 + ''; 24 + }; 25 + 26 + serverAddr = lib.mkOption { 27 + type = lib.types.nullOr lib.types.str; 28 + default = null; 29 + example = "https://solaceon:6443"; 30 + }; 31 + 32 + tlsSans = lib.mkOption { 33 + type = lib.types.listOf lib.types.str; 34 + default = ["solaceon" "eterna" "pastoria"]; 35 + }; 36 + 37 + zone = lib.mkOption { 38 + type = lib.types.nullOr lib.types.str; 39 + default = null; 40 + example = "cloud-hetzner"; 41 + }; 42 + 43 + ingress = lib.mkEnableOption "cute.haus/ingress=true node label"; 44 + }; 45 + 46 + config = lib.mkMerge [ 47 + { 48 + sops.secrets.k3s = { 49 + sopsFile = "${self}/secrets/k3s.yaml"; 50 + key = "TOKEN"; 51 + }; 52 + 53 + # systemd-oomd fights kubelet's eviction manager 54 + systemd.oomd.enable = lib.mkForce false; 55 + 56 + networking.firewall.allowedTCPPorts = lib.mkIf cfg.ingress [80 443 2222]; 57 + 58 + services = { 59 + k3s = { 60 + enable = true; 61 + inherit (cfg) role clusterInit; 62 + serverAddr = lib.mkIf (cfg.serverAddr != null) cfg.serverAddr; 63 + tokenFile = config.sops.secrets.k3s.path; 64 + extraFlags = 65 + ["--flannel-iface=tailscale0"] 66 + ++ lib.optionals (cfg.role == "server") ( 67 + [ 68 + "--service-node-port-range=8000-32767" 69 + "--disable=traefik" 70 + "--disable=servicelb" 71 + ] 72 + ++ map (san: "--tls-san=${san}") cfg.tlsSans 73 + ) 74 + ++ lib.optionals cfg.clusterInit ["--write-kubeconfig-mode=644"] 75 + ++ lib.optionals (cfg.zone != null) ["--node-label=topology.kubernetes.io/zone=${cfg.zone}"] 76 + ++ lib.optionals cfg.ingress ["--node-label=cute.haus/ingress=true"]; 77 + }; 78 + 79 + openiscsi = { 80 + enable = true; 81 + name = "iqn.2026-05.haus.cute:${config.networking.hostName}"; 82 + }; 83 + }; 84 + 85 + environment.systemPackages = with pkgs; [ 86 + helmfile 87 + kubernetes-helm 88 + nfs-utils 89 + ]; 90 + 91 + systemd = { 92 + # Longhorn instance-manager looks for binaries in /usr/local/bin 93 + tmpfiles.rules = [ 94 + "L+ /usr/local/bin - - - - /run/current-system/sw/bin/" 95 + ]; 96 + 97 + services = { 98 + # Block k3s startup until tailscale0 has its IP — flannel-iface=tailscale0 99 + # otherwise races and etcd peer setup fails on cold boot. 100 + k3s = { 101 + after = ["tailscaled.service"]; 102 + wants = ["tailscaled.service"]; 103 + serviceConfig.ExecStartPre = pkgs.writeShellScript "wait-tailscale0" '' 104 + until ${pkgs.iproute2}/bin/ip -4 addr show tailscale0 | grep -q inet; do 105 + ${pkgs.coreutils}/bin/sleep 1 106 + done 107 + ''; 108 + }; 109 + 110 + # Cleanly log out iSCSI sessions at shutdown so reboots don't hang 111 + # waiting for udev scsi_id timeouts against dead longhorn devices. 112 + iscsi-logout = { 113 + description = "Log out iSCSI sessions cleanly at shutdown"; 114 + after = ["iscsid.service"]; 115 + before = ["k3s.service"]; 116 + requires = ["iscsid.service"]; 117 + wantedBy = ["multi-user.target"]; 118 + serviceConfig = { 119 + Type = "oneshot"; 120 + RemainAfterExit = true; 121 + ExecStart = "${pkgs.coreutils}/bin/true"; 122 + ExecStop = "-${pkgs.openiscsi}/bin/iscsiadm -m node -u"; 123 + TimeoutStopSec = "30s"; 124 + }; 125 + }; 126 + }; 127 + }; 128 + } 129 + 130 + (lib.optionalAttrs (options ? myBackups) { 131 + myBackups.jobs.k3s = lib.mkIf (cfg.role == "server") { 132 + backupPrepareCommand = "${config.services.k3s.package}/bin/k3s etcd-snapshot save"; 133 + paths = [ 134 + "/var/lib/rancher/k3s/server/db/snapshots" 135 + "/var/lib/rancher/k3s/server/cred" 136 + "/var/lib/rancher/k3s/server/tls" 137 + ]; 138 + }; 139 + }) 140 + ]; 141 + }; 142 + }
+24
nix/nixos/profiles/swap.nix
··· 1 + _: { 2 + flake.modules.nixos.swap = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.mySwap.size = lib.mkOption { 8 + default = 8192; 9 + description = "Swap size in megabytes."; 10 + type = lib.types.int; 11 + }; 12 + 13 + config = { 14 + swapDevices = [ 15 + { 16 + device = "/.swap"; 17 + priority = 0; 18 + randomEncryption.enable = true; 19 + inherit (config.mySwap) size; 20 + } 21 + ]; 22 + }; 23 + }; 24 + }
+17
nix/nixos/profiles/vps.nix
··· 1 + _: { 2 + flake.modules.nixos.vps = { 3 + documentation = { 4 + enable = false; 5 + nixos.enable = false; 6 + }; 7 + 8 + services.journald = { 9 + storage = "persistent"; 10 + 11 + extraConfig = '' 12 + SystemMaxUse=500M 13 + MaxRetentionSec=1week 14 + ''; 15 + }; 16 + }; 17 + }
+9
nix/nixos/profiles/zram.nix
··· 1 + _: { 2 + flake.modules.nixos.zram = { 3 + zramSwap = { 4 + enable = true; 5 + algorithm = "zstd"; 6 + priority = 100; 7 + }; 8 + }; 9 + }
+125
nix/nixos/programs/nix.nix
··· 1 + _: { 2 + flake.modules.nixos.nix-config = { 3 + config, 4 + lib, 5 + options, 6 + ... 7 + }: let 8 + buildMachines = [ 9 + { 10 + hostName = "jubilife"; 11 + maxJobs = 12; 12 + protocol = "ssh-ng"; 13 + speedFactor = 5; 14 + sshKey = "/etc/ssh/ssh_host_ed25519_key"; 15 + sshUser = "nixbuild"; 16 + supportedFeatures = ["nixos-test" "benchmark" "big-parallel" "kvm"]; 17 + systems = ["x86_64-linux"]; 18 + } 19 + ]; 20 + 21 + isBuildMachine = lib.elem config.networking.hostName (lib.map (m: m.hostName) buildMachines); 22 + in 23 + lib.mkMerge [ 24 + { 25 + nix = { 26 + buildMachines = lib.mkIf config.services.tailscale.enable ( 27 + lib.filter (m: m.hostName != config.networking.hostName) buildMachines 28 + ); 29 + 30 + distributedBuilds = true; 31 + 32 + gc = { 33 + automatic = true; 34 + options = 35 + if isBuildMachine 36 + then "--delete-older-than 20d" 37 + else "--delete-older-than 3d"; 38 + persistent = true; 39 + randomizedDelaySec = "60min"; 40 + }; 41 + 42 + extraOptions = '' 43 + min-free = ${toString (1 * 1024 * 1024 * 1024)} 44 + max-free = ${toString (5 * 1024 * 1024 * 1024)} 45 + ''; 46 + 47 + optimise = { 48 + automatic = true; 49 + persistent = true; 50 + randomizedDelaySec = "60min"; 51 + }; 52 + 53 + settings = { 54 + builders-use-substitutes = true; 55 + 56 + experimental-features = [ 57 + "fetch-closure" 58 + "flakes" 59 + "nix-command" 60 + ]; 61 + 62 + substituters = [ 63 + "https://cache.nixos.org/" 64 + "https://alyraffauf.cachix.org" 65 + "https://catppuccin.cachix.org" 66 + "https://chaotic-nyx.cachix.org/" 67 + "https://cutehaus.cachix.org" 68 + "https://nix-community.cachix.org" 69 + ]; 70 + 71 + trusted-public-keys = [ 72 + "alyraffauf.cachix.org-1:GQVrRGfjTtkPGS8M6y7Ik0z4zLt77O0N25ynv2gWzDM=" 73 + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" 74 + "catppuccin.cachix.org-1:noG/4HkbhJb+lUAdKrph6LaozJvAeEEZj4N732IysmU=" 75 + "chaotic-nyx.cachix.org-1:HfnXSw4pj95iI/n17rIDy40agHj12WfF+Gqk6SonIT8" 76 + "cutehaus.cachix.org-1:KiifTsseQBitoaHH8rkDUDwzyz9akLeOM+K+e2eK8dA=" 77 + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 78 + ]; 79 + 80 + trusted-users = ["aly" "@admin" "@wheel" "nixbuild"]; 81 + }; 82 + }; 83 + 84 + programs.nix-ld.enable = true; 85 + 86 + users.users.nixbuild = lib.mkIf isBuildMachine { 87 + uid = 1999; 88 + isNormalUser = true; 89 + createHome = false; 90 + group = "nixbuild"; 91 + }; 92 + 93 + users.groups.nixbuild = lib.mkIf isBuildMachine {}; 94 + } 95 + 96 + (lib.optionalAttrs (options ? mySshKeys) { 97 + mySshKeys.authorizedUsers.nixbuild = lib.mkIf isBuildMachine ["aly" "root"]; 98 + }) 99 + 100 + { 101 + myRecipes.nix = '' 102 + # Garbage collect Nix store 103 + [group('nix')] 104 + gc-nix days="3": 105 + @echo "Cleaning up Nix generations older than {{days}} days..." 106 + sudo nix-collect-garbage --delete-older-than {{days}}d 107 + 108 + # Optimize Nix store 109 + [group('nix')] 110 + optimize-nix: 111 + @echo "Optimizing Nix store..." 112 + sudo nix-store --optimise 113 + 114 + # Free space from Nix store 115 + [group('nix')] 116 + cleanup-nix: gc-nix && optimize-nix 117 + 118 + # Repair Nix store 119 + [group('nix')] 120 + repair-nix: 121 + sudo nix-store --repair --verify --check-contents 122 + ''; 123 + } 124 + ]; 125 + }
+125
nix/nixos/programs/njust.nix
··· 1 + _: { 2 + flake.modules.nixos.njust = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: let 8 + cfg = config.myNjust; 9 + 10 + defaultRecipes = { 11 + system = '' 12 + # Show system info 13 + [group('system')] 14 + info: 15 + @echo "Hostname: $(hostname)" 16 + @echo "NixOS Version: $(nixos-version)" 17 + @echo "Kernel: $(uname -r)" 18 + @echo "Generation: $(sudo nix-env --list-generations -p /nix/var/nix/profiles/system | tail -1 | awk '{print $1}')" 19 + @echo "Revision: $(nixos-version --json | jq -r '.configurationRevision // "unknown"')" 20 + ''; 21 + 22 + updates = '' 23 + # Update everything 24 + [group('system')] 25 + update: update-nixos update-nix-profile 26 + 27 + # Update NixOS system 28 + [group('nix')] 29 + update-nixos action="switch": 30 + @echo "Updating NixOS..." 31 + sudo nixos-rebuild {{action}} --flake "${config.myFlakeUrl}" 32 + 33 + # Update Nix user profile 34 + [group('nix')] 35 + update-nix-profile: 36 + @echo "Updating Nix user profile..." 37 + nix profile upgrade --all 38 + ''; 39 + 40 + secureboot = '' 41 + # Check Secure Boot status 42 + [group('secureboot')] 43 + sb-status: 44 + sudo bootctl status 45 + 46 + # Generate Secure Boot keys 47 + [group('secureboot')] 48 + gen-sb-keys: 49 + @echo "Generating Secure Boot keys..." 50 + sudo sbctl create-keys 51 + 52 + # Enroll Secure Boot keys 53 + [group('secureboot')] 54 + enroll-sb-keys: 55 + sudo sbctl enroll-keys --microsoft 56 + ''; 57 + 58 + full-disk-encryption = '' 59 + # List encrypted volumes 60 + [group('encryption')] 61 + fde-status: 62 + lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,TYPE,UUID | grep crypt 63 + 64 + # Enable TPM2 disk unlock 65 + [group('encryption')] 66 + [confirm("Verify Secure Boot is active before continuing!")] 67 + enable-tpm2-unlock crypt="/dev/nvme0n1p2": 68 + sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+2+7+12 --wipe-slot=tpm2 {{crypt}} 69 + 70 + # Enable FIDO2 disk unlock 71 + [group('encryption')] 72 + enable-fido2-unlock crypt="/dev/nvme0n1p2": 73 + sudo systemd-cryptenroll --fido2-device=auto {{crypt}} 74 + ''; 75 + }; 76 + 77 + recipes = config.myRecipes // lib.optionalAttrs cfg.defaultRecipes defaultRecipes; 78 + 79 + mergedJustfileContent = '' 80 + _default: 81 + @printf '\033[1;36mnjust\033[0m\n' 82 + @printf 'Just-based recipe runner for NixOS.\n\n' 83 + @printf '\033[1;33mUsage:\033[0m njust <recipe> [args...]\n\n' 84 + @njust --list --list-heading $'Available recipes:\n\n' 85 + 86 + ${lib.concatStringsSep "\n" (lib.attrValues recipes)} 87 + ''; 88 + 89 + validatedJustfile = 90 + pkgs.runCommand "njust-justfile-validated" { 91 + nativeBuildInputs = [pkgs.just]; 92 + preferLocalBuild = true; 93 + } '' 94 + echo ${lib.escapeShellArg mergedJustfileContent} > justfile 95 + 96 + echo "Validating njust justfile syntax..." 97 + just --justfile justfile --summary >/dev/null || { 98 + echo "ERROR: njust justfile has syntax errors!" 99 + echo "Justfile content:" 100 + cat justfile 101 + exit 1 102 + } 103 + 104 + cp justfile $out 105 + echo "njust justfile validation passed" 106 + ''; 107 + 108 + njustScript = pkgs.writeShellApplication { 109 + name = "njust"; 110 + runtimeInputs = [pkgs.jq pkgs.just]; 111 + text = '' 112 + exec just --working-directory "$PWD" --justfile ${validatedJustfile} "$@" 113 + ''; 114 + }; 115 + in { 116 + options.myNjust = { 117 + defaultRecipes = lib.mkOption { 118 + type = lib.types.bool; 119 + default = true; 120 + }; 121 + }; 122 + 123 + config.environment.systemPackages = [njustScript]; 124 + }; 125 + }
+21
nix/nixos/programs/podman.nix
··· 1 + _: { 2 + flake.modules.nixos.podman = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: { 8 + environment.systemPackages = lib.optionals config.services.xserver.enable [pkgs.pods]; 9 + 10 + virtualisation = { 11 + oci-containers.backend = "podman"; 12 + 13 + podman = { 14 + enable = true; 15 + autoPrune.enable = true; 16 + defaultNetwork.settings.dns_enabled = true; 17 + dockerCompat = true; 18 + }; 19 + }; 20 + }; 21 + }
+16
nix/nixos/programs/systemd-boot.nix
··· 1 + _: { 2 + flake.modules.nixos.systemd-boot = {lib, ...}: { 3 + boot = { 4 + initrd.systemd.enable = lib.mkDefault true; 5 + 6 + loader = { 7 + efi.canTouchEfiVariables = lib.mkDefault true; 8 + 9 + systemd-boot = { 10 + enable = lib.mkDefault true; 11 + configurationLimit = lib.mkDefault 10; 12 + }; 13 + }; 14 + }; 15 + }; 16 + }
+8
nix/nixos/recipes.nix
··· 1 + _: { 2 + flake.modules.nixos.recipes = {lib, ...}: { 3 + options.myRecipes = lib.mkOption { 4 + type = lib.types.attrsOf lib.types.str; 5 + default = {}; 6 + }; 7 + }; 8 + }
+49
nix/nixos/services/alloy.nix
··· 1 + _: { 2 + flake.modules.nixos.alloy = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.myAlloy.lokiUrl = lib.mkOption { 8 + description = "Loki URL to report to"; 9 + default = "https://loki.narwhal-snapper.ts.net/loki/api/v1/push"; 10 + type = lib.types.str; 11 + }; 12 + 13 + config = { 14 + services.alloy.enable = true; 15 + 16 + environment.etc."alloy/config.alloy".text = '' 17 + loki.write "default" { 18 + endpoint { 19 + url = "${config.myAlloy.lokiUrl}" 20 + } 21 + } 22 + 23 + loki.relabel "journal" { 24 + forward_to = [] 25 + 26 + rule { 27 + source_labels = ["__journal__systemd_unit"] 28 + target_label = "unit" 29 + } 30 + 31 + rule { 32 + source_labels = ["__journal__systemd_user_unit"] 33 + target_label = "user_unit" 34 + } 35 + } 36 + 37 + loki.source.journal "read" { 38 + forward_to = [loki.write.default.receiver] 39 + relabel_rules = loki.relabel.journal.rules 40 + max_age = "12h" 41 + labels = { 42 + job = "systemd-journal", 43 + host = "${config.networking.hostName}", 44 + } 45 + } 46 + ''; 47 + }; 48 + }; 49 + }
+47
nix/nixos/services/atbbs.nix
··· 1 + _: { 2 + flake.modules.nixos.atbbs = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.myAtbbs = { 8 + port = lib.mkOption { 9 + description = "Port to listen on."; 10 + default = 8582; 11 + type = lib.types.int; 12 + }; 13 + 14 + telnetPort = lib.mkOption { 15 + description = "Port to listen on for telnet."; 16 + default = 2323; 17 + type = lib.types.int; 18 + }; 19 + }; 20 + 21 + config = { 22 + networking.firewall.allowedTCPPorts = [ 23 + config.myAtbbs.port 24 + config.myAtbbs.telnetPort 25 + ]; 26 + 27 + virtualisation.oci-containers = { 28 + backend = "podman"; 29 + 30 + containers = { 31 + atbbs = { 32 + extraOptions = ["--pull=always"]; 33 + image = "ghcr.io/alyraffauf/atbbs"; 34 + environment.PUBLIC_URL = "https://atbbs.xyz"; 35 + ports = ["0.0.0.0:${toString config.myAtbbs.port}:80"]; 36 + }; 37 + 38 + atbbs-telnet = { 39 + extraOptions = ["--pull=always"]; 40 + image = "ghcr.io/alyraffauf/atbbs-telnet"; 41 + ports = ["0.0.0.0:${toString config.myAtbbs.telnetPort}:2323"]; 42 + }; 43 + }; 44 + }; 45 + }; 46 + }; 47 + }
+13
nix/nixos/services/cachefilesd.nix
··· 1 + _: { 2 + flake.modules.nixos.cachefilesd = { 3 + services.cachefilesd = { 4 + enable = true; 5 + 6 + extraConfig = '' 7 + brun 20% 8 + bcull 10% 9 + bstop 5% 10 + ''; 11 + }; 12 + }; 13 + }
+77
nix/nixos/services/forgejo-runner.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.forgejo-runner = { 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: { 8 + options.myForgejoRunner = { 9 + nativeRunners = lib.mkOption { 10 + type = lib.types.int; 11 + default = 1; 12 + description = "How many native NixOS runners to run."; 13 + }; 14 + 15 + dockerContainers = lib.mkOption { 16 + type = lib.types.int; 17 + default = 1; 18 + description = "How many docker containers to run."; 19 + }; 20 + }; 21 + 22 + config = { 23 + assertions = [ 24 + { 25 + assertion = config.services.tailscale.enable; 26 + message = "We contact Forĝejo over tailscale, but services.tailscale.enable != true."; 27 + } 28 + ]; 29 + 30 + sops.secrets.act-runner = { 31 + sopsFile = "${self}/secrets/act-runner.yaml"; 32 + key = "TOKEN"; 33 + }; 34 + 35 + services.gitea-actions-runner = let 36 + arch = lib.replaceStrings ["-"] ["_"] pkgs.stdenv.hostPlatform.system; 37 + in { 38 + instances = let 39 + tokenFile = config.sops.secrets.act-runner.path; 40 + in { 41 + alycodes-containers = { 42 + inherit tokenFile; 43 + enable = true; 44 + labels = lib.optional (arch == "aarch64_linux") "ubuntu-24.04-arm:docker://gitea/runner-images:ubuntu-latest" ++ lib.optional (arch == "x86_64_linux") "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"; 45 + name = "${arch}-${config.networking.hostName}-alycodes-containers"; 46 + 47 + settings = { 48 + container.network = "host"; 49 + runner.capacity = config.myForgejoRunner.dockerContainers; 50 + }; 51 + 52 + url = "https://git.aly.codes"; 53 + }; 54 + 55 + alycodes-nixos = { 56 + inherit tokenFile; 57 + enable = true; 58 + 59 + hostPackages = with pkgs; 60 + [bash cachix coreutils curl gawk gitMinimal gnused jq nodejs wget] 61 + ++ [config.nix.package]; 62 + 63 + labels = ["nixos-${arch}:host"]; 64 + name = "${arch}-${config.networking.hostName}-alycodes-nixos"; 65 + 66 + settings = { 67 + container.network = "host"; 68 + runner.capacity = config.myForgejoRunner.nativeRunners; 69 + }; 70 + 71 + url = "https://git.aly.codes"; 72 + }; 73 + }; 74 + }; 75 + }; 76 + }; 77 + }
+86
nix/nixos/services/plex.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.plex = { 3 + config, 4 + lib, 5 + options, 6 + pkgs, 7 + ... 8 + }: let 9 + stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 10 + start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 11 + in { 12 + options.myPlex.dataDir = lib.mkOption { 13 + description = "Data directory to use."; 14 + default = "/var/lib"; 15 + type = lib.types.str; 16 + }; 17 + 18 + config = lib.mkMerge [ 19 + { 20 + services.plex = { 21 + enable = true; 22 + dataDir = "${config.myPlex.dataDir}/plex"; 23 + 24 + extraPlugins = [ 25 + (builtins.path { 26 + name = "Audnexus.bundle"; 27 + path = self.inputs.audnexus; 28 + }) 29 + (builtins.path { 30 + name = "Hama.bundle"; 31 + path = self.inputs.hama; 32 + }) 33 + ]; 34 + 35 + extraScanners = [ 36 + (builtins.path { 37 + name = "Absolute-Series-Scanner"; 38 + path = self.inputs.absolute; 39 + }) 40 + ]; 41 + 42 + openFirewall = true; 43 + }; 44 + 45 + systemd.services.plex.serviceConfig.TimeoutStopSec = 15; 46 + } 47 + 48 + (lib.optionalAttrs (options ? myBackups) { 49 + myBackups.jobs.plex = { 50 + backupCleanupCommand = start "plex"; 51 + backupPrepareCommand = stop "plex"; 52 + exclude = ["${config.services.plex.dataDir}/Plex Media Server/Plug-in Support/Databases"]; 53 + paths = [config.services.plex.dataDir]; 54 + }; 55 + }) 56 + ]; 57 + }; 58 + 59 + flake.modules.nixos.tautulli = { 60 + config, 61 + lib, 62 + options, 63 + pkgs, 64 + ... 65 + }: let 66 + stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 67 + start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 68 + in { 69 + config = lib.mkMerge [ 70 + { 71 + services.tautulli = { 72 + enable = true; 73 + openFirewall = true; 74 + }; 75 + } 76 + 77 + (lib.optionalAttrs (options ? myBackups) { 78 + myBackups.jobs.tautulli = { 79 + backupCleanupCommand = start "tautulli"; 80 + backupPrepareCommand = stop "tautulli"; 81 + paths = [config.services.tautulli.dataDir]; 82 + }; 83 + }) 84 + ]; 85 + }; 86 + }
+17
nix/nixos/services/prometheusNode.nix
··· 1 + _: { 2 + flake.modules.nixos.prometheus-node = { 3 + services.prometheus.exporters.node = { 4 + enable = true; 5 + enabledCollectors = ["systemd"]; 6 + 7 + extraFlags = [ 8 + "--collector.ethtool" 9 + "--collector.softirqs" 10 + "--collector.tcpstat" 11 + "--collector.wifi" 12 + ]; 13 + 14 + port = 3021; 15 + }; 16 + }; 17 + }
+62
nix/nixos/services/qbittorrent.nix
··· 1 + _: { 2 + flake.modules.nixos.qbittorrent = { 3 + config, 4 + lib, 5 + options, 6 + pkgs, 7 + ... 8 + }: let 9 + cfg = config.myQbittorrent; 10 + stop = service: "${pkgs.systemd}/bin/systemctl stop ${service}"; 11 + start = service: "${pkgs.systemd}/bin/systemctl start ${service}"; 12 + in { 13 + options.myQbittorrent = { 14 + dataDir = lib.mkOption { 15 + type = lib.types.path; 16 + default = "/var/lib/qbittorrent"; 17 + }; 18 + 19 + user = lib.mkOption { 20 + type = lib.types.str; 21 + default = "qbittorrent"; 22 + }; 23 + group = lib.mkOption { 24 + type = lib.types.str; 25 + default = "qbittorrent"; 26 + }; 27 + port = lib.mkOption { 28 + type = lib.types.port; 29 + default = 8080; 30 + }; 31 + openFirewall = lib.mkOption { 32 + type = lib.types.bool; 33 + default = false; 34 + }; 35 + 36 + package = lib.mkOption { 37 + type = lib.types.package; 38 + default = pkgs.qbittorrent-nox; 39 + defaultText = lib.literalExpression "pkgs.qbittorrent-nox"; 40 + }; 41 + }; 42 + 43 + config = lib.mkMerge [ 44 + { 45 + services.qbittorrent = { 46 + inherit (cfg) openFirewall user group package; 47 + enable = true; 48 + profileDir = cfg.dataDir; 49 + webuiPort = cfg.port; 50 + }; 51 + } 52 + 53 + (lib.optionalAttrs (options ? myBackups) { 54 + myBackups.jobs.qbittorrent = { 55 + backupCleanupCommand = start "qbittorrent"; 56 + backupPrepareCommand = stop "qbittorrent"; 57 + paths = [config.services.qbittorrent.profileDir]; 58 + }; 59 + }) 60 + ]; 61 + }; 62 + }
+134
nix/nixos/services/syncthing.nix
··· 1 + _: { 2 + flake.modules.nixos.syncthing = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.mySyncthing = { 8 + certFile = lib.mkOption { 9 + description = "Path to the certificate file."; 10 + type = lib.types.path; 11 + }; 12 + 13 + keyFile = lib.mkOption { 14 + description = "Path to the key file."; 15 + type = lib.types.path; 16 + }; 17 + 18 + romsPath = lib.mkOption { 19 + default = "/home/${config.mySyncthing.user}/roms"; 20 + description = "Path to the ROM folder."; 21 + type = lib.types.path; 22 + }; 23 + 24 + syncROMs = lib.mkEnableOption "Whether to sync ROMs."; 25 + 26 + user = lib.mkOption { 27 + description = "User to run Syncthing as."; 28 + type = lib.types.str; 29 + }; 30 + }; 31 + 32 + config = { 33 + systemd.services.syncthing.environment.STNODEFAULTFOLDER = "true"; 34 + 35 + services = { 36 + caddy.virtualHosts = 37 + lib.mkIf 38 + (config.services.caddy.enable && config.services.tailscale.enable) 39 + { 40 + "syncthing-${config.networking.hostName}.narwhal-snapper.ts.net" = { 41 + extraConfig = '' 42 + bind tailscale/syncthing-${config.networking.hostName} 43 + reverse_proxy localhost:8384 { 44 + header_up Host localhost 45 + } 46 + ''; 47 + }; 48 + }; 49 + 50 + syncthing = let 51 + cfg = config.mySyncthing; 52 + 53 + devices = { 54 + "allyx" = {id = "XTEVJX2-DBEFN4X-UCG43YR-V4FOQYU-DMM2WH4-AMCC5FS-42UB3DM-KUDVHQL";}; 55 + "eterna" = {id = "ZAD2MVO-I2OQII4-C3T756B-BEBQMM6-Q4ILH2H-5CR3TMI-DR4VBFD-GLRVOQK";}; 56 + "fallarbor" = {id = "P4URLH4-YWLMO6J-W62ET7H-TQAO3Y6-T2FAYOY-C2VTI65-VQXHVGG-NQ76PAZ";}; 57 + "fortree" = {id = "S6PVA3I-EKOCGIU-GFX7AE6-FXM45OW-JTYN5LJ-UZ4LADZ-NNAJGDD-KST2VAG";}; 58 + "groudon" = {id = "VOEAEAG-NP5Z3BM-DK5FO75-6G4NKSJ-3EUNFSV-VIR4KDH-OM6ZN7L-OOQKCQJ";}; 59 + "jubilife" = {id = "52MTCMC-PKEWSAU-HADMTZU-DY5EKFO-B323P7V-OBXLNTQ-EJY7F7Y-EUWFBQX";}; 60 + "kyogre" = {id = "SBQNUXS-H4XDJ3E-RBHJPT5-45WDJJA-2U43M4P-23XGUJ7-E3CNNKZ-BXSGIA3";}; 61 + "oreburgh" = {id = "RFVF6DA-CQJLXTP-RKMYEB3-D2KMWJH-3Z2CIAN-PNYOXI6-FIDBFWG-JJA57AX";}; 62 + "pacifidlog" = {id = "6EBVXYI-HZW4LQI-T6L3TTI-DZEBXJM-RP3DW7N-BCAG6FC-G2654DN-XJFSLQD";}; 63 + "petalburg" = {id = "O75EK2H-YBXPM5D-PBYV7XB-DJKFL3E-OFZBB7H-MLCD2UT-NXQRMDG-BTZZQQH";}; 64 + "rp5" = {id = "5AKCXRS-XU7BBSS-RDPSSLG-4BDOZ4K-OXLFQZX-HJSM53W-4GLSOAC-RZ7APA7";}; 65 + "rpclassic" = {id = "EDNFUWI-UFYEOPI-QPJIEWF-NXVEGZ6-2J7IXW7-X22L27F-VU6JKSB-CH6GDAO";}; 66 + "rustboro" = {id = "NY53BFH-CPVCXGH-MI5AT7E-WBK7TXS-5NQDSCW-J5BALLV-EGS2VJL-CMED2AH";}; 67 + "slateport" = {id = "MDJFDUG-UJAXQXI-AMEF2AR-PBMD5QK-Z5ZG6AA-RCJCU3M-GZHQQEA-X2JGOAK";}; 68 + "snowpoint" = {id = "TFSZWZB-EDIFV2P-333APP2-T655TM4-2XGA7QA-P22Z36W-3RNGX2C-DLETAQ7";}; 69 + "sootopolis" = {id = "7QGSZ2D-CGMLPWD-OCFCBXP-7746W7F-COFV52F-Q2PMCAS-GV5DCSV-6NIXDQJ";}; 70 + "thor" = {id = "B33X5WA-S6P4XEE-VURE4PB-WKMXHLP-6AG55LZ-QIG6ZJG-GDGXWAD-EDVIRAF";}; 71 + "verdanturf" = {id = "CQ7A2KW-2JRZHEO-NF6NZLY-C2OX4SO-EPKQRMV-7YBSSFA-FJ2CW2P-NOIKPQB";}; 72 + }; 73 + 74 + folders = lib.mkMerge [ 75 + { 76 + "sync" = { 77 + devices = ["allyx" "eterna" "fallarbor" "fortree" "groudon" "jubilife" "kyogre" "oreburgh" "pacifidlog" "petalburg" "rustboro" "slateport" "snowpoint" "sootopolis" "verdanturf"]; 78 + id = "default"; 79 + path = "~/sync"; 80 + versioning = { 81 + params.cleanoutDays = "5"; 82 + type = "trashcan"; 83 + }; 84 + }; 85 + 86 + "screenshots" = { 87 + devices = ["fallarbor" "oreburgh" "jubilife" "pacifidlog" "petalburg" "rustboro" "slateport" "snowpoint" "sootopolis" "verdanturf"]; 88 + id = "screenshots"; 89 + path = "~/pics/screenshots"; 90 + versioning = { 91 + params.cleanoutDays = "5"; 92 + type = "trashcan"; 93 + }; 94 + }; 95 + 96 + "roms" = { 97 + devices = ["jubilife" "oreburgh" "pacifidlog" "petalburg" "rp5" "rpclassic" "rustboro" "sootopolis" "thor"]; 98 + id = "emudeck"; 99 + versioning = { 100 + params.cleanoutDays = "3"; 101 + type = "trashcan"; 102 + }; 103 + }; 104 + } 105 + { 106 + "roms" = { 107 + enable = cfg.syncROMs; 108 + path = cfg.romsPath; 109 + }; 110 + } 111 + ]; 112 + in { 113 + enable = true; 114 + cert = cfg.certFile; 115 + configDir = "${config.services.syncthing.dataDir}/.syncthing"; 116 + dataDir = "/home/${cfg.user}"; 117 + key = cfg.keyFile; 118 + openDefaultPorts = true; 119 + inherit (cfg) user; 120 + 121 + settings = { 122 + options = { 123 + localAnnounceEnabled = true; 124 + relaysEnabled = true; 125 + urAccepted = -1; 126 + }; 127 + 128 + inherit devices folders; 129 + }; 130 + }; 131 + }; 132 + }; 133 + }; 134 + }
+92
nix/nixos/services/tailscale.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.tailscale = { 3 + config, 4 + lib, 5 + ... 6 + }: { 7 + options.myTailscale = { 8 + authKeyFile = lib.mkOption { 9 + description = "Key file to use for authentication"; 10 + default = config.sops.secrets.tailscaleAuthKey.path or null; 11 + type = lib.types.nullOr lib.types.path; 12 + }; 13 + 14 + operator = lib.mkOption { 15 + description = "Tailscale operator name"; 16 + default = null; 17 + type = lib.types.nullOr lib.types.str; 18 + }; 19 + }; 20 + 21 + config = lib.mkMerge [ 22 + { 23 + sops.secrets.tailscaleAuthKey = { 24 + sopsFile = "${self}/secrets/tailscale.yaml"; 25 + key = "auth_key"; 26 + }; 27 + 28 + assertions = [ 29 + { 30 + assertion = config.myTailscale.authKeyFile != null; 31 + message = "config.myTailscale.authKeyFile cannot be null."; 32 + } 33 + ]; 34 + 35 + networking.firewall = { 36 + allowedUDPPorts = [config.services.tailscale.port]; 37 + trustedInterfaces = [config.services.tailscale.interfaceName]; 38 + }; 39 + 40 + services = { 41 + # When caddy is also enabled, expose a tailnet-hostname vhost that 42 + # proxies the local syncthing UI through /syncthing/. 43 + caddy = lib.mkIf config.services.caddy.enable { 44 + virtualHosts."${config.networking.hostName}.narwhal-snapper.ts.net".extraConfig = lib.concatLines (lib.optional config.services.syncthing.enable '' 45 + redir /syncthing /syncthing/ 46 + handle_path /syncthing/* { 47 + reverse_proxy localhost:8384 { 48 + header_up Host localhost 49 + } 50 + } 51 + ''); 52 + }; 53 + 54 + tailscale = { 55 + enable = true; 56 + inherit (config.myTailscale) authKeyFile; 57 + 58 + extraUpFlags = 59 + ["--ssh"] 60 + ++ lib.optional (config.myTailscale.operator != null) 61 + "--operator ${config.myTailscale.operator}"; 62 + 63 + openFirewall = true; 64 + permitCertUid = lib.mkIf config.services.caddy.enable "caddy"; 65 + useRoutingFeatures = "both"; 66 + }; 67 + }; 68 + } 69 + 70 + { 71 + myRecipes.tailscale = '' 72 + # Connect to Mullvad NYC 73 + [group('tailscale')] 74 + enable-mullvad: 75 + @echo "Connecting to Mullvad NYC exit node..." 76 + tailscale set --exit-node=us-nyc-wg-301.mullvad.ts.net 77 + 78 + # Disconnect from Mullvad NYC 79 + [group('tailscale')] 80 + disable-mullvad: 81 + @echo "Disconnecting from exit node..." 82 + tailscale set --exit-node= 83 + 84 + # Show Tailscale status 85 + [group('tailscale')] 86 + ts-status: 87 + tailscale status 88 + ''; 89 + } 90 + ]; 91 + }; 92 + }
+40
nix/nixos/ssh-keys.nix
··· 1 + {self, ...}: { 2 + flake.modules.nixos.ssh-keys = { 3 + config, 4 + lib, 5 + ... 6 + }: let 7 + keyFiles = builtins.attrNames (builtins.readDir "${self}/keys"); 8 + filesFor = keys: builtins.concatMap (key: config.mySshKeys.keys.${key} or []) keys; 9 + in { 10 + options.mySshKeys = { 11 + keys = lib.mkOption { 12 + type = lib.types.attrsOf (lib.types.listOf lib.types.path); 13 + description = "Cached SSH key file paths, read once to avoid repeated filesystem scans."; 14 + default = { 15 + aly = lib.map (file: "${self}/keys/${file}") (lib.filter (file: lib.hasPrefix "aly_" file) keyFiles); 16 + root = lib.map (file: "${self}/keys/${file}") (lib.filter (file: lib.hasPrefix "root_" file) keyFiles); 17 + }; 18 + }; 19 + 20 + authorizedUsers = lib.mkOption { 21 + type = lib.types.attrsOf (lib.types.listOf lib.types.str); 22 + default = { 23 + root = ["aly"]; 24 + }; 25 + }; 26 + }; 27 + 28 + config = lib.mkMerge [ 29 + { 30 + users.users.root.openssh.authorizedKeys.keyFiles = filesFor (config.mySshKeys.authorizedUsers.root or []); 31 + } 32 + (lib.mkIf (config.mySshKeys.authorizedUsers ? aly) { 33 + users.users.aly.openssh.authorizedKeys.keyFiles = filesFor config.mySshKeys.authorizedUsers.aly; 34 + }) 35 + (lib.mkIf (config.mySshKeys.authorizedUsers ? nixbuild) { 36 + users.users.nixbuild.openssh.authorizedKeys.keyFiles = filesFor config.mySshKeys.authorizedUsers.nixbuild; 37 + }) 38 + ]; 39 + }; 40 + }
+58
nix/nixos/users.nix
··· 1 + _: { 2 + flake.modules.nixos.users = { 3 + config, 4 + lib, 5 + options, 6 + pkgs, 7 + ... 8 + }: { 9 + options.myUsers = { 10 + defaultGroups = lib.mkOption { 11 + description = "Default groups for desktop users."; 12 + default = [ 13 + "cdrom" 14 + "dialout" 15 + "docker" 16 + "libvirtd" 17 + "lp" 18 + "networkmanager" 19 + "plugdev" 20 + "scanner" 21 + "transmission" 22 + "video" 23 + "wheel" 24 + ]; 25 + }; 26 + 27 + aly.password = lib.mkOption { 28 + type = lib.types.nullOr lib.types.str; 29 + default = null; 30 + description = "Hashed password for aly."; 31 + }; 32 + }; 33 + 34 + config = lib.mkMerge [ 35 + { 36 + programs.fish.enable = true; 37 + 38 + users = { 39 + defaultUserShell = pkgs.fish; 40 + mutableUsers = false; 41 + 42 + users = { 43 + aly = { 44 + description = "Aly Raffauf"; 45 + extraGroups = config.myUsers.defaultGroups; 46 + hashedPassword = config.myUsers.aly.password; 47 + isNormalUser = true; 48 + uid = 1000; 49 + }; 50 + }; 51 + }; 52 + } 53 + (lib.optionalAttrs (options ? mySshKeys) { 54 + mySshKeys.authorizedUsers.aly = ["aly"]; 55 + }) 56 + ]; 57 + }; 58 + }
+1 -1
scripts/update-caddy-tailscale.ts
··· 10 10 11 11 import { $ } from "bun"; 12 12 13 - const CADDY_FILE = "nix/modules/nixos/services/caddy/default.nix"; 13 + const CADDY_FILE = "nix/nixos/services/caddy.nix"; 14 14 const GITHUB_REPO = "tailscale/caddy-tailscale"; 15 15 const NIX_BUILD_TARGET = 16 16 ".#nixosConfigurations.jubilife.config.services.caddy.package";