diff --git a/configuration.nix b/configuration.nix index 0bd6310..fd4f84b 100644 --- a/configuration.nix +++ b/configuration.nix @@ -7,7 +7,7 @@ ./desktop.nix ./nvidia.nix ./shell.nix - # ./yami-dev-stack.nix # uncomment to enable Yami dev stack, then set services.yami-dev-stack below + ./yami-dev-stack.nix ]; nix = { @@ -98,5 +98,11 @@ # Docker is now managed by yami-dev-stack.nix when that module is enabled. # services.yami-dev-stack = { enable = true; user = "chexit"; }; + services.yami-dev-stack = { + enable = true; + user = "chexit"; + dataDir = "/mnt/ssd_110/yami"; + }; + system.stateVersion = "25.11"; } diff --git a/yami-dev-stack.nix b/yami-dev-stack.nix index f1777ad..77b0f0e 100644 --- a/yami-dev-stack.nix +++ b/yami-dev-stack.nix @@ -13,9 +13,20 @@ in type = types.str; description = "User that will own runner token and be added to docker group"; }; + + dataDir = mkOption { + type = types.nullOr types.path; + default = null; + example = "/mnt/ssd_110/yami"; + description = '' + If set, bind-mounts /var/lib/{gitea,postgresql,gitea-runner} under this + path so all stack data lives on a separate disk. Directories are created + automatically; existing data must be rsync'd manually before enabling. + ''; + }; }; - config = mkIf cfg.enable { + config = mkIf cfg.enable (mkMerge [ { # ── Systemd target ───────────────────────────────────────────────────────── # Manual-only: nothing in wantedBy, so it never starts on boot. @@ -128,7 +139,11 @@ in enable = true; name = "home-runner"; url = "http://localhost:3000"; - tokenFile = "/var/lib/gitea-runner/token"; + # NOT under /var/lib/gitea-runner — that path is systemd-managed for the + # service (DynamicUser + StateDirectory means it becomes a symlink to + # /var/lib/private/gitea-runner). Pre-creating it ourselves breaks the + # service with "Failed to set up special execution directory: File exists". + tokenFile = "/var/lib/gitea-runner-secret/token"; labels = [ "ubuntu-latest:docker://node:20" "native:host" @@ -143,12 +158,13 @@ in }; # Runner uses DynamicUser, so there's no static gitea-runner user/group. - # tokenFile is loaded via systemd EnvironmentFile, so the file MUST be in - # env-file format, not a raw token: + # Token lives in a sibling dir (not /var/lib/gitea-runner — that's systemd's + # StateDirectory and pre-creating it breaks DynamicUser bind setup). + # The file MUST be in env-file format, not a raw token: # TOKEN= # Get the token from Site Administration → Actions → Runners → Create new Runner. systemd.tmpfiles.rules = [ - "d /var/lib/gitea-runner 0700 root root -" + "d /var/lib/gitea-runner-secret 0700 root root -" ]; # ── Shell aliases ────────────────────────────────────────────────────────── @@ -162,5 +178,31 @@ in # ── Firewall ─────────────────────────────────────────────────────────────── # Merged with existing rules (Steam etc.); does not overwrite them. networking.firewall.allowedTCPPorts = [ 3000 2222 ]; - }; + } + + # ── Optional: move all stack data to a separate disk via bind-mounts ───────── + (mkIf (cfg.dataDir != null) ( + let + # gitea-runner intentionally NOT bind-mounted: its service uses + # DynamicUser + StateDirectory, which refuses to adopt an existing + # mount point ("Failed to set up special execution directory: File exists"). + # Its data is tiny (token + .runner state) so it stays on the system disk. + subs = [ "gitea" "postgresql" ]; + mkBind = name: { + name = "/var/lib/${name}"; + value = { + device = "${cfg.dataDir}/${name}"; + fsType = "none"; + options = [ "bind" ]; + }; + }; + in { + # Ensure target dirs exist on the data disk before mounts happen. + systemd.tmpfiles.rules = map (n: "d ${cfg.dataDir}/${n} 0755 root root -") subs + ++ [ "d ${cfg.dataDir} 0755 root root -" ]; + + fileSystems = listToAttrs (map mkBind subs); + } + )) + ]); }