3.4 KiB
Strategy
Pattern
Pull-based GitOps via Gitea Actions runners on the owner host.
dev box ── git push ──► Gitea (Charlie/<stack>) ── webhook ──► runner on owner host
│
▼
git -C /mnt/nas/stacks/<stack> pull --ff-only
docker compose pull && docker compose up -d
The git working tree at /mnt/nas/stacks/<stack>/ is the deployment. Runtime state lives separately under /mnt/nas/data/<stack>/ (see 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-onlywill refuse to clobber. - Runtime state never enters git. Compose files bind into
/mnt/nas/data/<stack>/, not into the working tree. .envignored,.env.exampletracked. Secrets stay out.- Image tags pinned. No
:latest. - Cross-host self-update: the runner that deploys
Charlie/gitealives 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.)
init
Stack is currently just a NAS folder. git init in the new /mnt/nas/stacks/<stack>/, push to Charlie/<name>. Recipe: migration.md.
rewrite
Code we author, currently a clone of a github.com repo. Move to /mnt/nas/stacks/<stack>/, git remote set-url origin to Charlie. Recipe: migration.md.
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/<name>-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.