Auto-Deploy on New Release
Arena’s release pipeline publishes a multi-arch container image to GHCR
and a SLSA build-provenance attestation on every v*.*.* tag. If your
host follows the three-stage pattern below, a new release turns into a
live container with zero manual steps.
The stages
Section titled “The stages”-
Cutting the release bumps the public compose file. The release commit moves
pyproject.toml,CHANGELOG.mdand the pinned tag indocker-compose.ymltogether, then gets tagged. Anyone who doesgit pullpicks up the new pin.A workflow job used to push that bump back to
mainon its own. It could never work:mainrequires status checks, the bot has no admin bypass, so the push was rejected withGH006, and its[skip ci]marker meant the required checks would never run on that commit either. Bumping in the release commit is atomic and needs no elevated token. -
Host-specific override (
docker-compose.override.yml, gitignored) keeps the actual pin your host is running. Docker Compose merges it in automatically. Never edit the basedocker-compose.ymlon the host — past drift bugs traced back to that. -
Cron-driven auto-deploy script on the host watches GHCR for a new semver tag, sed-bumps the override, does
docker compose pull && up -d, waits forHEALTHCHECKto go green, and pings Discord. On unhealthy: restores the backup override, restarts, re-checks, pings 🔴.
What ships in this repo
Section titled “What ships in this repo”docker-compose.yml— image-based (pinned tag, auto-bumped byrelease.yml)docker-compose.dev.yml— override for local build-from-sourcedeploy/autodeploy.env.example— template for the per-service config the auto-deploy script sourcesdeploy/docker-compose.override.example.yml— starter host-specific override with an image pin and optional host-networking snippetrelease.yml— publishes the image, attests provenance (SLSA v1, cosign keyless via OIDC — no long-lived keys), and auto-bumps the compose example
Verifying the attestation
Section titled “Verifying the attestation”Every image tag ships with a SLSA build-provenance attestation as a
GHCR registry referrer. Verify from any host with cosign v3+:
cosign verify-attestation \ --type slsaprovenance1 \ --certificate-identity-regexp \ '^https://github.com/pete-builds/open-model-arena/.github/workflows/release.yml@refs/tags/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/pete-builds/open-model-arena@<digest>Use --type slsaprovenance1 (cosign v3), not the bare slsaprovenance
alias — the attestation is v1.0.
Onboarding a host
Section titled “Onboarding a host”Assumes: nix1-style deploy host with the generic auto-deploy script
already installed (see ~/scripts/mcp-autodeploy.sh — one script, many
services). New services just drop a config file + a staggered cron entry.
git clonethe repo into~/docker/model-arena/cp deploy/docker-compose.override.example.yml docker-compose.override.yml(fill in the image pin)cp .env.example .env, fill inARENA_PASSPHRASE+AUTH_TOKEN_SECRET(+ optionallyARENA_API_TOKENSfor headless / metrics)docker compose up -d; visit the host at port 3694cp deploy/autodeploy.env.example ~/scripts/mcp-autodeploy/model-arena.env,chmod 600, edit the Discord webhook URL- Add a staggered cron entry (existing services live at
:05,:10, etc. past 5am; pick a fresh minute):30 5 * * * /home/pete/scripts/mcp-autodeploy.sh model-arena \>> /home/pete/scripts/mcp-autodeploy/model-arena.log 2>&1 - Smoke test:
bash ~/scripts/mcp-autodeploy.sh model-arena— should exit silently with “Already on latest”
Notification philosophy
Section titled “Notification philosophy”The auto-deploy script is deliberately quiet on no-ops. Discord pings only fire on:
- ✅ Successful deploy (new version live + healthy)
- 🔴 Failed deploy (rolled back to previous)
- 🚨 Rollback failed (needs hands-on)
- Container unhealthy without a deploy attempt
Daily “everything fine” runs produce zero noise. That’s the point — if Discord pings, something happened.