# Forgejo on the Swarm cluster Runs as `deploy/forgejo/docker-stack.yml` — the only Swarm-scheduled workload this cluster carries (the Thermograph app itself stays on the Terraform-managed `docker compose` deploys; see `terraform/README.md`). Pinned to the **beta** node (old VPS) via the `role=forge` label from `deploy/swarm/label-forge-node.sh`. The Actions **runner** is deliberately *not* part of this stack — it runs on the **desktop** as a plain systemd service (`register-lan-runner.sh` below), per `docs/runbooks/implementation-handoff.md` Track B step 5. That's the canonical placement; an earlier revision of this stack ran the runner as a Swarm-scheduled Docker-in-Docker sidecar pinned to beta, which is gone now. ## Prerequisites 1. All three nodes have joined the swarm (`deploy/swarm/`) and beta is labeled `role=forge`. 2. `docker node ls` (from the manager) shows all three `Ready`. ## One-time setup: Swarm secret One secret the stack expects to already exist (a Swarm secret, not a file — `external: true` in the stack file, so `docker stack deploy` never creates or sees the value, only references it): ```bash # A strong random password for Forgejo's own Postgres (NOT related to # Thermograph's app database — entirely separate instance/network). openssl rand -base64 32 | docker secret create forgejo_db_password - ``` ## Deploy / update ```bash docker stack deploy -c deploy/forgejo/docker-stack.yml forgejo ``` Re-running is safe — Swarm only touches services whose spec actually changed. ## Register the Actions runner (on the desktop, not through Swarm) Once Forgejo answers at its domain: ```bash # On the desktop: # Forgejo web UI -> Site Administration -> Actions -> Runners -> Create new Runner # (or, for a repo-scoped runner: -> Settings -> Actions -> Runners) # copy the registration token, then: bash deploy/forgejo/register-lan-runner.sh https:// ``` See that script's header for exactly what it replaces (the pre-Forgejo GitHub self-hosted runner on this same machine) and why it registers with two labels where there used to be two separate runners. ## DNS Point the Forgejo domain (default `git.thermograph.org`; override with `FORGEJO_DOMAIN=...` before `docker stack deploy`, Swarm reads it from the deploying shell's environment) at **prod's or beta's** public IP — not the desktop's, which isn't a stable publicly-reachable address. The routing mesh forwards published ports to wherever the task actually landed, so either VPS IP works. ## Why Postgres here and not the Thermograph app's TimescaleDB Separate instance, separate network (`forgejo_net`, not the app's compose network), separate volume. Forgejo is a distinct product with its own schema and its own backup/restore lifecycle — sharing a database with the app would couple two things that should be able to fail, migrate, and restore independently. ## Verifying ```bash docker service ls # forgejo_db, forgejo_forgejo, forgejo_traefik all Running, 1/1 curl -I https://git.thermograph.org/ # 200, valid cert # On the desktop, after registering the runner: systemctl --user status forgejo-runner # active, both labels registered ``` ## Rollback / removal ```bash docker stack rm forgejo # volumes (forgejo_data, forgejo_db, ...) survive a stack rm — remove them # explicitly only if you actually want to destroy the Forgejo instance's data. ```