# Strategy ## Pattern Pull-based GitOps via Gitea Actions runners on the owner host. ``` dev box ── git push ──► Gitea (Charlie/) ── webhook ──► runner on owner host │ ▼ git -C /mnt/nas/stacks/ pull --ff-only docker compose pull && docker compose up -d ``` The git working tree at `/mnt/nas/stacks//` **is** the deployment. Runtime state lives separately under `/mnt/nas/data//` (see [layout.md](layout.md)). ## Rules - One owner host per stack. Only that host's runner ever touches the working tree. - No hand-edits on the host once a stack is repo-managed. `git pull --ff-only` will refuse to clobber. - Runtime state never enters git. Compose files bind into `/mnt/nas/data//`, not into the working tree. - `.env` ignored, `.env.example` tracked. Secrets stay out. - Image tags pinned. No `:latest`. - Cross-host self-update: the runner that deploys `Charlie/gitea` lives on morgott; the runner that updates morgott's runner lives on melina. A runner never redeploys itself mid-job. ## Repo types Three. (We dropped `upstream+overrides` — see [Why no upstream cloning](#why-no-upstream-cloning).) ### `init` Stack is currently just a NAS folder. `git init` in the new `/mnt/nas/stacks//`, push to `Charlie/`. Recipe: [migration.md](migration.md#init). ### `rewrite` Code we author, currently a clone of a github.com repo. Move to `/mnt/nas/stacks//`, `git remote set-url origin` to Charlie. Recipe: [migration.md](migration.md#rewrite). ### `mirror` Pull-mirror of an upstream we don't customise (or want as offline backup). Configured via Gitea's pull-mirror feature. Read-only, never deployed from. Naming: `Charlie/-mirror`. ## Why no upstream cloning Previously we considered cloning upstream's source on the host and layering a `docker-compose.override.yml`. Dropped because: - For every "upstream image" stack (`invidious`, `searxng`, `victoriametrics`, `mealie`, `paperless-ngx`), we only ever ran the published image. Their source tree is dead weight on the host. - Override-on-template means the upstream's compose can change under us silently. - Two-remote / rebase / force-push dance was the price for dubious value. Instead: our `docker-compose.yml` references the published image directly. It is the only compose. Upstream's compose is a *reference document* we read in their mirror repo when we want to see what changed. ## Image-update flow (Phase 3) Diun watches registries; on a tag/digest change it POSTs to a Gitea webhook that triggers a `redeploy` workflow with `force-pull: true` on the owner host. Replaces watchtower. ## Secrets Open question. Candidate: vaultwarden as the source, with a runner-side fetch step that materialises `.env` per job. Decided when we design the runner stack. ## Why not Portainer/Komodo/Dockge - **Portainer Stacks-from-Git**: works, but no submodule support, opinionated about working-tree location, adds a control plane on top of git. - **Komodo**: nice, also pull-based, also relocates working trees. Reconsider if we ever want a UI. - **Dockge**: file mgmt only, no git wiring. The runner-driven pattern keeps us in plain `git` + `docker compose` and adds nothing the team doesn't already understand.