# ───────────────────────────────────────────────────────────────────────── # traefik.env.template — AUTHORITATIVE template for traefik/traefik.env # (rendered by deploy/provision-stack.py per secrets/secrets-map.yaml) # # - This file IS the complete key list for traefik.env. The whole file is # rendered on every provisioning run — no line surgery, so a key can # never silently go missing. # - Non-secret config lives here as LITERAL values (visible, reviewable). # - Secret values are dollar-brace placeholders resolved from the CI env # (Woodpecker from_secret vars) at provisioning time. provision-stack.py # FAILS HARD if any placeholder is missing/empty. # - The rendered traefik/traefik.env exists only on the host (gitignored). # - Rendered by provision-stack.py, NOT Woodpecker's yaml preprocessor — # single-dollar placeholders are safe here (deploy.yml's double-dollar # rule does NOT apply to this file). # # Consumed by traefik/traefik.yaml (KEEPALIVED_PASSWORD, KEEPALIVED_VIRTUAL_IPS). # DOMAIN comes from deploy/global.env, not here. # # ⚠️ HIGH RISK: Traefik is the entry point for all homelab services, and the # Keepalived VIP (192.168.4.30) depends on this stack. KEEPALIVED_PASSWORD # MUST be identical across keepalived-master and keepalived-backup — both # consume this same rendered value — or VRRP auth fails and the VIP # becomes unstable (this is exactly what the 2026-09-12 incident was: # a committed literal "***REDACTED***" placeholder in git, restored every # time the file was deleted/re-synced, diverging from -backup's stale but # correct in-memory value). # # Classic VRRP simple-auth is silently truncated to 8 characters by # keepalived itself. Set TRAEFIK_KEEPALIVED_PASSWORD in Woodpecker to a # value 8 characters or fewer (or accept that only the first 8 chars are # actually significant) so the effective negotiated value is unambiguous. # # 2026-09-12 RE-TRIGGER: traefik.env was found MISSING on disk after the # Pattern C migration merged — the manifest-driven provisioning render only # happens on a push that changes traefik/, and traefik is bootstrap-tier # (stack-deploy.sh must be run manually; the pipeline never auto-deploys # this stack). A manual `stack-deploy.sh traefik` had apparently been run # while traefik.env was absent, causing Swarm's own interpolation to # silently render KEEPALIVED_PASSWORD/KEEPALIVED_VIRTUAL_IPS as EMPTY # STRINGS on keepalived-master (not missing — empty, which is worse, # since it's a valid-looking VRRP auth string that mismatches everything). # This comment-only change re-triggers provision-secrets to re-render and # re-ship traefik.env; a manual `stack-deploy.sh traefik` must still be run # afterward to actually apply it to the running services. # ───────────────────────────────────────────────────────────────────────── # ── Keepalived (non-secret config) ──────────────────────────────────────── KEEPALIVED_VIRTUAL_IPS="#PYTHON2BASH:['192.168.4.30']" # ── Keepalived (secret) ──────────────────────────────────────────────────── KEEPALIVED_PASSWORD=${TRAEFIK_KEEPALIVED_PASSWORD}