Commit Graph
100 Commits
Author SHA1 Message Date
admin d7fe56f6fe ai: add doc comment noting secrets are now provisioned via Woodpecker
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
No functional change -- this comment-only edit exists to trigger a real
deploy of the ai stack so provision-secrets' ai) case, and the AI_
var-name fix from PR #7, get exercised end-to-end for the first time.

Documents that MCPO_API_KEY is intentionally still manual/unmigrated.
2026-08-25 23:48:47 -07:00
admin 6af2633936 fix(ai): write AI_-prefixed AWS key names to ai.env, not plain names
ai.yaml's litellm service (as of commit 37ed671a, "Change AWS keys to
use Woodpecker Secrets") references ${AI_AWS_ACCESS_KEY_ID} /
${AI_AWS_SECRET_ACCESS_KEY} and renders them into the container as
plain AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY. The ai) provisioning
case added in the earlier secrets-migration PR wrote the plain
(unprefixed) names into ai.env instead, which would leave
${AI_AWS_ACCESS_KEY_ID} unresolved at compose-render time (renders
empty) -- silently breaking Bedrock auth in litellm on the next ai
stack deploy.

Fixed both the grep -vE exclusion pattern and the two printf lines to
use the AI_-prefixed names. All other migrated vars in ai.yaml use
plain names and are unaffected.

No other changes in this file.
2026-08-25 23:42:39 -07:00
admin 5188aef250 hotfix: restore $${VAR} double-dollar escaping for all secret-backed vars
The AI secrets migration PR (#4) was a full-file rewrite of deploy.yml.
That rewrite mechanically dropped one $ from EVERY $${VAR} occurrence in
the file, not just the new AI additions -- silently reverting all
pre-existing secret references (SWARM_MANAGER_IP, IMMICH_*, GIT_*,
POSTGRESQL_*, VAULTWARDEN_*, ENTERTAINMENT_*, etc.) to single-dollar
form. Per this file's own header comment, Woodpecker blanks single-dollar
braced refs at compile time since secrets aren't in that variable map --
this is the exact "SWARM_MANAGER_IP secret is empty" failure mode
documented above, and it fired immediately on the first push after #4
merged.

Impact: provision-secrets/deploy/verify all failed at their first
if-empty guard and exited before any ssh/scp/rsync ran. No live secret,
service, or deployed stack was touched -- this was a CI-only outage.

Fix: restored $${VAR} for every secret-backed reference throughout the
file. CI_PIPELINE_FILES / CI_COMMIT_MESSAGE stay single-dollar (correct
-- those are Woodpecker compile-time metadata, not secrets). The \$FILE
/ \$TMP backslash-escaping inside the ai) case's remote SSH command is
unrelated and was already correct (it protects those local-to-remote
vars from expanding before the SSH payload is sent).

This is a straight revert-of-the-regression -- no new secrets, no logic
changes beyond restoring the escaping.
2026-08-25 23:27:47 -07:00
admin d4797d4b4b docs: note ai stack secrets in SECRETS.md (trivial commit to force clean pipeline run)
ci/woodpecker/push/deploy Pipeline failed
No stack files changed -- this commit exists only to trigger a fresh
Woodpecker pipeline run against current main + current secrets, since
"Restart" on the prior failed run was replaying a stale snapshot from
before the ai_* secrets existed.
2026-08-25 23:19:14 -07:00
admin 2959721d3c ai: migrate AWS/LiteLLM/OpenWebUI/OAuth secrets from ai.env to Woodpecker secrets
Adds 8 new from_secret-backed env vars to provision-secrets and rewrites
the `ai)` case to do a targeted update of only those 8 keys in the
remote ai/ai.env via grep -v + printf (no sed, safe for values containing
/, $, &, etc). All other lines in ai.env (MCPO_API_KEY, OAUTH_CLIENT_ID,
WEBUI_URL, etc.) are left completely untouched -- MCPO_API_KEY migration
is deferred to a follow-up per plan, and this change never reads or
writes that value.

New secrets required in Woodpecker (Settings -> Secrets) before merge:
  ai_aws_access_key_id
  ai_aws_secret_access_key
  ai_litellm_master_key
  ai_litellm_salt_key
  ai_litellm_db_password
  ai_webui_secret_key
  ai_open_webui_database_url
  ai_oauth_client_secret
2026-08-25 22:40:05 -07:00
admin 52cecf2cf9 vaultwarden: rotate admin_token secret to v2 (hashed value)
ci/woodpecker/push/deploy Pipeline failed
2026-08-25 21:56:53 -07:00
admin 2b9209d9d1 Deprecate: hwaccel.transcoding.yml - functionality moved to main immich.yml
ci/woodpecker/push/deploy Pipeline failed
ci/woodpecker/cron/renovate Pipeline was successful
2026-08-19 23:03:53 -07:00
admin 4907039db9 Deprecate: hwaccel.ml.yml - functionality moved to main immich.yml
ci/woodpecker/push/deploy Pipeline was canceled
2026-08-19 23:03:52 -07:00
admin bef97dd908 Fix: Add /dev/dri to immich-server for quicksync transcoding support
ci/woodpecker/push/deploy Pipeline failed
2026-08-19 23:03:37 -07:00
admin 7af75c43ed Revert: Use volumes instead of devices for DDM compatibility with Docker Swarm
ci/woodpecker/push/deploy Pipeline failed
2026-08-19 23:02:06 -07:00
admin 472dd3a34a Revert: Use volumes instead of devices for DDM compatibility with Docker Swarm
ci/woodpecker/push/deploy Pipeline was canceled
2026-08-19 23:02:04 -07:00
admin d145aeeebe feat: add woodpecker-mcp.mjs server script for MCPO
ci/woodpecker/push/deploy Pipeline failed
ci/woodpecker/cron/renovate Pipeline was successful
2026-08-18 22:54:06 -07:00
admin 284bf98a6d Fix: Correct hwaccel.ml.yml to extend immich-machine-learning with devices, not create separate service
ci/woodpecker/push/deploy Pipeline failed
ci/woodpecker/cron/renovate Pipeline was successful
2026-08-18 07:51:58 -07:00
admin 623af9577c Fix: Correct hwaccel.transcoding.yml to extend immich-server with devices, not create separate service
ci/woodpecker/push/deploy Pipeline was canceled
2026-08-18 07:51:49 -07:00
admin a0a1a87e04 Remove: Delete immich.env (should not be version controlled)
ci/woodpecker/push/deploy Pipeline failed
2026-08-17 23:38:07 -07:00
admin 0349a948e5 Add: Create immich.env-example with placeholder values
ci/woodpecker/push/deploy Pipeline failed
2026-08-17 23:37:41 -07:00
admin 38ace2a109 Add: Create immich.env with required environment variables
ci/woodpecker/push/deploy Pipeline failed
2026-08-17 23:35:18 -07:00
admin 6ccd4cc34a Fix: Add missing image to quicksync service in hwaccel.transcoding.yml
ci/woodpecker/push/deploy Pipeline was canceled
2026-08-17 23:35:18 -07:00
admin 47e9f95fcb Fix: Add missing image to openvino service in hwaccel.ml.yml
ci/woodpecker/push/deploy Pipeline was canceled
2026-08-17 23:35:17 -07:00
admin aa87f11588 cutover: fix missing PGPASSWORD on all remote psql -h calls (Phases 3/5/8)
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
Root cause of the "Canary row did not propagate" Phase 3 failure (run
#7): legacy's pg_hba.conf requires scram-sha-256 for any non-local
connection, and every remote `psql -h <patroni-node>` call in this
script had never supplied a password at all. This was masked until the
prior stderr-capture fix (run #7) surfaced the real error:
"fe_sendauth: no password supplied" on every single attempt.

patroni-0/patroni-1 use the SAME PGadmin superuser + SAME
postgresql_password secret as legacy itself (per
postgresql-ha-staging.yaml), so the fix reads that secret once via
`docker exec "$LEGACY_CID" cat /run/secrets/postgresql_password` early
in Phase 3, then passes it to every remote psql call via
`docker exec -e PGPASSWORD=...` (not spliced into the bash -c string,
to avoid quoting hazards).

Found and fixed the identical missing-password pattern in THREE
places, all with the same root cause:
- Phase 3: the canary-propagation SELECT (where it was first caught)
- Phase 5: the post-promotion pg_is_in_recovery() check
- Phase 8: the alias write + both leader/replica visibility checks

Added "2026 run #8" entry to the script's own header FIX LOG. Not yet
re-validated by a run reaching past Phase 3.

See ADR-0001 note, Session Update 11 (to be added).
2026-08-08 14:01:57 -07:00
admin dbabc69c2a cutover: surface stderr in Phase 3's canary-propagation check (diagnostic fix)
ci/woodpecker/push/deploy Pipeline was successful
Phase 3's "Canary row did not propagate to <replica> within 5s" failure
has now recurred twice (2026 run #4, root-caused as the Phase 2 lag-check
bug; and the run immediately after the Ceph IOPS fix, cause unconfirmed)
with genuinely healthy Phase 1/2 beforehand both times. The per-attempt
SELECT against the replica was discarding stderr entirely (2>/dev/null),
so a real connection/auth error and a genuine multi-second replication
delay were indistinguishable in the log — both just showed "row not
found".

Each of the 5 propagation-check attempts now captures stderr to
/tmp/cutover_phase3_attempt_<N>.stderr (mirrors the existing Phase 8
pattern) and echoes result+stderr into the log per-attempt. The final
trigger_rollback() message on failure includes the last non-empty
stderr seen directly in the FAIL line. The initial canary write's
stderr is also no longer discarded. No behavior change to timing/retry
counts — purely additive diagnostics.

See ADR-0001 note, Session Update 10 (to be added).
2026-08-08 13:07:53 -07:00
admin 2ed406db82 rollback: add full-session logging (writes to same dir as backup)
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
Persists this run's entire stdout/stderr transcript to BACKUP_DIR
(/volume1/SMB-docker/backup) — the same directory the pg_dumpall
backup lands in. Appends to an already-open CUTOVER_SESSION_LOG if
invoked as a child of cutover.sh (merging into that session's single
transcript); opens its own rollback-standalone-<TS>.log if run
standalone (including a manual run long after the fact, per this
script's own asymmetry warning). This is the single highest-value
place for a durable transcript in the whole suite, since rollback.sh
failing partway is the one scenario RUNBOOK.md flags as requiring
manual intervention. Also logs the transcript path in the grace-window
refusal message so it's not lost even in that failure mode.
Console/SSH output unchanged (tee mirrors to both).

See ADR-0001 note, Session Update 9.
2026-08-05 15:08:47 -07:00
admin 653f308623 cutover: add full-session logging (writes to same dir as backup)
ci/woodpecker/push/deploy Pipeline was successful
Persists the ENTIRE multi-phase transcript (Pre-Phase-0 through Phase
11, including Phase 0's preflight.sh output and any automatically
triggered rollback.sh output) to a single timestamped
cutover-session-<TS>.log in BACKUP_DIR (/volume1/SMB-docker/backup) —
the same directory the pg_dumpall backup lands in. Exports
CUTOVER_SESSION_LOG so child preflight.sh/rollback.sh invocations
append to the same file instead of opening their own. Adds an EXIT
trap that always announces final exit code + log path, specifically
so the one scenario RUNBOOK.md flags as needing manual intervention
(rollback.sh itself failing partway) is still fully investigable
after the fact even without a live terminal. Console/SSH output is
unchanged (tee mirrors to both).

See ADR-0001 note, Session Update 9.
2026-08-05 15:07:22 -07:00
admin fcbaa2cba3 cutover: add full-session logging to preflight.sh (writes to same dir as backup)
ci/woodpecker/push/deploy Pipeline was successful
Persists this run's entire stdout/stderr transcript to BACKUP_DIR
(/volume1/SMB-docker/backup), the SAME location as the pg_dumpall
backup file itself, so a failed run can be investigated later even
without a live terminal attached. Appends to an already-open
CUTOVER_SESSION_LOG if invoked as a child of cutover.sh (one merged
multi-phase transcript per session); opens its own
preflight-standalone-<TS>.log if run directly. Also logs the repo's
git HEAD at cutover/ for traceability, per the ADR-0001 note's
local-checkout-drift lesson (Session Update 8).

See ADR-0001 note, Session Update 9.
2026-08-05 15:03:14 -07:00
admin 82a8414ffa cutover.sh: interactive stale-state cleanup prompt before Phase 0
ci/woodpecker/push/deploy Pipeline was successful
Real incident: a run had both patroni-0 and patroni-1 stuck forever on
"waiting for standby_leader to bootstrap", never even attempting to race
for the role. Root cause was leftover etcd/patroni data on disk from a
prior interrupted run (operator stopped it short) — the postgresqlha
stack itself was gone, but etcd-1/2/3-data still had persisted raft state
including a real /service/postgres-ha/initialize key and old replication
slot records. Fresh Patroni nodes booting against that non-fresh etcd
correctly concluded the cluster already existed and deferred forever
waiting for a leader that could never appear, since nobody actually held
the lock. rollback.sh's own data-dir wipe only fires when it detects the
HA stack IS currently present (Case D/E) — if the stack was already gone
by the time cleanup ran, its Case A path never touches the data dirs,
leaving exactly this trap.

Added a "Pre-Phase-0" check that runs before preflight.sh's ~6+ minute
pg_dumpall: detects a still-present postgresqlha stack OR non-empty
etcd-*/patroni-*-data left over from a prior run, and — since this is
destructive and the operator explicitly wants this to be a deliberate
choice, not silent automatic cleanup — prompts interactively before
tearing down/wiping. Non-interactive sessions (no tty) hard-fail with a
clear message rather than guessing; AUTO_CLEANUP=yes in the environment
skips the prompt for deliberate unattended re-runs. Legacy production
data is never touched by any of this.
2026-08-05 13:37:15 -07:00
admin b2b86a668d cutover.sh: fix Phase 2's lag check — was a silent no-op
ci/woodpecker/push/deploy Pipeline was successful
Root cause of a real run's failure: Phase 2 passed (patroni-0 reached
standby_leader, patroni-1's basebackup completed, role flipped to
"replica"), but Phase 3's canary write then failed to propagate within
5s moments later. patroni_lag() queried the REPLICA's own /patroni
endpoint for lag data — but that field only exists on the LEADER side
(derived from pg_stat_replication); a replica's own /patroni response
never has it. So $LAG_INFO was always empty, and Phase 2's gate
`[ -z "$LAG_INFO" ] || ...` short-circuited permanently true — the lag
check never actually ran. Phase 2 degraded to "did role flip to replica
3x in a row", which can be true before the replica has genuinely caught
up on WAL backlog from its own basebackup.

Fixed by replacing patroni_lag() with cluster_member_lag_state(), which
queries the LEADER's /cluster endpoint (real pg_stat_replication-backed
data, same shape verified in the original dry run) and extracts the
specific replica's state/lag fields from its member object. Phase 2 now
requires literal state=streaming AND (lag=0 or absent), not just "field
was empty because we asked the wrong node." Phase 4's informational lag
log line updated to match.
2026-08-05 08:11:15 -07:00
admin 8f860ab14d postgresql-ha-final.yaml: add matching primary_slot_name (keep byte-identical with staging)
ci/woodpecker/push/deploy Pipeline was successful
Mirrors the fix in postgresql-ha-staging.yaml: added
primary_slot_name: standby_leader_slot to both patroni-0 and patroni-1's
standby_cluster blocks so this file's specs stay byte-identical to
staging's, per this file's own "do not recreate the live promoted
primary/replica" design requirement. See postgresql-ha-staging.yaml
header and the ADR-0001 note for full incident detail.
2026-08-04 20:04:50 -07:00
admin 7a06b040ad postgresql-ha-staging.yaml: add primary_slot_name to fix real bootstrap failure
ci/woodpecker/push/deploy Pipeline was successful
Root cause of a real cutover run's failure: the standby_leader's basebackup
from legacy completed, but its Postgres process then got permanently stuck
in "starting" because legacy had no replication slot reserving WAL — normal
WAL recycling (checkpoint_timeout=300s) deleted the segment needed to
resume streaming during the 8-11 min basebackup window. This also explained
why the cascade replica's own basebackup (which targets the standby_leader)
failed with "database system is starting up" — one root cause, not two.

Fix: created a physical replication slot (standby_leader_slot) on live
production legacy, and added primary_slot_name: standby_leader_slot under
bootstrap.dcs.standby_cluster in both patroni-0 and patroni-1's
SPILO_CONFIGURATION so Patroni pins the slot automatically. Also bumped
wal_keep_size to 4GB on legacy as defense-in-depth.

Full incident detail documented in this file's header for future reference.
2026-08-04 20:04:01 -07:00
admin e57f4b3592 cutover.sh: extend Phase 2 cascade-replica wait window to 35 min
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
Phase 2's cascade-replica bootstrap does its own full basebackup FROM the
new standby_leader (an extra hop beyond Phase 1's legacy-direct copy), so
per operator request its window is extended further than Phase 1's —
from 1200s (20 min, matched to observed 8-11 min legacy-direct timing) to
2100s (35 min), giving more margin for the additional hop. Phase 1's
window is intentionally left unchanged at 1200s since it already has
comfortable headroom against the timing we've actually observed for that
specific bootstrap path.
2026-08-04 00:48:32 -07:00
admin 29cd73eabc cutover.sh: dynamic standby_leader detection + extended bootstrap wait windows
ci/woodpecker/push/deploy Pipeline was successful
Two real-run bugs found and fixed:

1. Phase 1 hardcoded patroni-1 as the expected standby_leader. The
   bootstrap-race winner is actually nondeterministic (Patroni/etcd lock
   race) — the original prod attempt had patroni-0 win it instead, which
   the dry run never exercised. Fixed by polling BOTH patroni-0 and
   patroni-1 each iteration and capturing whichever wins into
   $LEADER_HOST, with the other becoming $REPLICA_HOST. Every later phase
   (2,3,4,5,6,8,10) now references $LEADER_HOST/$REPLICA_HOST instead of
   hardcoded hostnames.

2. Phase 1's wait window (240s) and Phase 2's (300s) were both far shorter
   than the observed real basebackup duration for a ~42GB cluster
   (8-11 minutes per prior dry-run/live polling). The role only flips to
   standby_leader/replica AFTER the full copy completes, so both timeouts
   could fire — and did — while a legitimate basebackup was still
   in-progress, triggering a false-negative rollback. Both windows
   extended to 1200s (20 min), with per-poll data-dir size logging
   (timeout-guarded du -sh) so progress is observable instead of a silent
   binary wait.
2026-08-04 00:40:20 -07:00
admin 9c1f31a70e ADR-0001 rollback.sh: accept any 2xx in consumer health checks (204 from Woodpecker was a false-positive failure)
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
2026-08-02 22:15:15 -07:00
admin 325daca86b ADR-0001 cutover.sh: fix Phase 1 node-label check (broken Go template on hyphenated label) and accept any 2xx in consumer health checks
ci/woodpecker/push/deploy Pipeline was successful
2026-08-02 22:14:17 -07:00
admin 066cbaf97f ADR-0001 Phase 3: add rollback.sh (standalone, idempotent, grace-window guard against post-cutover data loss)
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
2026-08-02 17:38:50 -07:00
admin 1203630bb5 ADR-0001 Phase 3: add cutover.sh (11-phase scripted cutover with auto-rollback)
ci/woodpecker/push/deploy Pipeline was successful
2026-08-02 17:32:42 -07:00
admin a4f5d54b82 ADR-0001 Phase 3: add postgresql-ha-final.yaml (Stage 3 - HAProxy alias handoff, no external port/Traefik yet)
ci/woodpecker/push/deploy Pipeline was successful
2026-08-02 17:28:26 -07:00
admin 01768d14f5 ADR-0001 Phase 3: add preflight.sh (disk/backup hard gates, catalog+consumer enumeration)
ci/woodpecker/push/deploy Pipeline was successful
2026-08-02 16:45:44 -07:00
admin f49b576806 ADR-0001 Phase 3: add cutover RUNBOOK (dependency map, phased procedure, rollback)
ci/woodpecker/push/deploy Pipeline was successful
2026-08-02 16:35:52 -07:00
admin 8d30f19c8c postgresql.yaml: update stale comment referencing old CLONE_* rationale to reference standby_cluster design (no functional change - this file never used CLONE_* itself)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-30 22:03:44 -07:00
admin 4dcb3561ed postgresql-ha-staging.yaml: switch from CLONE_WITH_BASEBACKUP to Patroni standby_cluster (continuous streaming, validated in pgha-test dry run) - closes pre-cutover write gap
ci/woodpecker/push/deploy Pipeline was successful
2026-07-30 21:59:44 -07:00
admin dc6120ffd4 pgha-dryrun.yaml: trigger secret re-provisioning after regenerating postgresql_replication_password (excludes &<>\" per pystache HTML-escaping bug found this session)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-30 21:44:49 -07:00
admin 8afe0049c4 pgha-dryrun.yaml: switch from CLONE_WITH_BASEBACKUP to Patroni standby_cluster (continuous streaming) to close the pre-cutover write gap
ci/woodpecker/push/deploy Pipeline was successful
2026-07-30 21:24:28 -07:00
admin 499402cd0d postgresql-ha-staging.yaml: port dry-run fixes (bugs 1,2,4,5) - $$(...) escaping (incl. CLONE_PASSWORD), ETCD3_HOSTS, post_init_wrapper.sh SUPERUSER role fix
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
2026-07-30 15:56:43 -07:00
admin 7e8e3388f4 postgresql.yaml: port dry-run fixes (bugs 1,2,4,5) - $$(...) escaping, ETCD3_HOSTS, post_init_wrapper.sh SUPERUSER role fix
ci/woodpecker/push/deploy Pipeline was successful
2026-07-30 15:55:26 -07:00
admin b7f0d9dbfb pgha dry-run: fix bug 5 - grant SUPERUSER to placeholder postgres role
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
2026-07-29 09:32:09 -07:00
admin e8e506dc4d add: post_init_wrapper.sh to work around Spilo's hardcoded 'postgres' role name assumption in post_init.sh. Does not fork/modify Spilo's script — creates missing role then execs the original unchanged.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-29 07:21:34 -07:00
admin 7529e6cb36 fix: override bootstrap.post_init via SPILO_CONFIGURATION to create missing 'postgres' role before Spilo's real post_init.sh runs. Spilo hardcodes ALTER VIEW...OWNER TO postgres with no way to parameterize, which fails since our superuser is PGadmin not postgres.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-29 06:35:24 -07:00
admin b86784fe3a fix: legacy container needs pg_hba.conf replication rule for pg_basebackup — added initdb.d hook script. Disposable test only, uses 'trust' since this container is not auth-representative.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-28 21:27:13 -07:00
admin 1dcb5ec467 fix: use ETCD3_HOSTS not ETCD_HOSTS — confirmed against spilo source that etcd/etcd3 are distinct DCS backends (v2 vs v3 API). Our etcd 3.5.9 containers have v2 API disabled, causing 404s with the old var name.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-28 21:10:13 -07:00
admin 83480aa23b fix: escape \$(...) as \$\$(...) in patroni command blocks — Compose interpolation was choking on \$( before the shell ever saw it (invalid interpolation format error)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-28 21:03:56 -07:00
admin 20e1210441 postgresql: add disposable dry-run test stack for cutover validation (pgha-test, own network + data dirs, zero prod impact)
ci/woodpecker/push/deploy Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
2026-07-28 11:12:19 -07:00
admin f4f0749969 postgresql: add cutover staging compose (ADR-0001 Phase 3). Lives in cutover/ subdir — deliberately excluded from stack-deploy.sh folder merge. Deployed only by cutover script as separate postgresqlha stack.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-28 11:07:03 -07:00
admin 87ae54fb3f postgresql: add HAProxy config for Patroni-aware TCP routing (ADR-0001 Phase 2)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-28 08:53:55 -07:00
admin c8d45e9e61 postgresql: add folder-based Patroni+etcd+HAProxy HA stack draft (ADR-0001 Phase 2). NOT deployed — bootstrap tier, manual deploy only. Coexists with flat postgresql.yaml until cutover.
ci/woodpecker/push/deploy Pipeline was canceled
2026-07-28 08:53:44 -07:00
admin 9a5b028e07 postgresql: document replication + patroni secrets (ADR-0001 Phase 1)
ci/woodpecker/push/deploy Pipeline failed
2026-07-28 07:20:52 -07:00
admin 0f26d31a02 postgresql: add replication + patroni Woodpecker secrets to provision-secrets step (ADR-0001 Phase 1)
ci/woodpecker/push/deploy Pipeline was canceled
2026-07-28 07:20:35 -07:00
admin 6789d2530a vaultwarden: retry #8 -- trigger after fixing pipeline compile error (dollar-brace literal in comments)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-27 13:36:17 -07:00
admin e0a6b7d233 deploy.yml: fix pipeline compile error 'missing closing brace' -- the documentation comment itself contained a literal dollar-brace sequence with invalid contents (dollar-brace CI_...), which Woodpecker's substitution engine parses even inside comments. Rewrote header to describe the rules in words without any literal dollar-brace sequences. Also removed backtick-quoted dollar-brace remnants from the vaultwarden case comment.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-27 13:35:13 -07:00
admin 9b3bba636c vaultwarden: retry #7 -- trigger after fixing Woodpecker compile-time ${VAR} substitution root cause 2026-07-27 12:52:59 -07:00
admin 6757b0dfe1 deploy.yml: THE ACTUAL ROOT CAUSE FIX. Woodpecker substitutes ${VAR} at pipeline-compile time from its CI metadata map, where secrets do not exist -- so every braced reference to a secret-backed env var (SWARM_MANAGER_IP, all provisioning values) was silently blanked before the shell ever ran. Proven by debug output showing eval "val=\$$v" arriving as val=\$v ($$ unescaped to $) and lengths matching var NAMES. Fix: escape all runtime secret refs as $${VAR}; keep ${CI_PIPELINE_FILES} braced (compile-time is correct for CI metadata). Removes flawed debug step; adds header documenting the substitution rules. 2026-07-27 12:50:21 -07:00
admin 7a81bc73bd deploy.yml: add temporary debug-secrets-all step declaring the identical 31-secret environment block as provision-secrets, printing byte length of every var (never values). Runs as its own step so it completes even when provision-secrets fails, telling us in one run whether all secrets are empty, only some, or only swarm_manager_ip.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 12:39:53 -07:00
admin 1a1068a154 vaultwarden: retry #6 -- re-trigger after fixing invalid 3DPRINT_DB_PASSWORD env var name
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 12:13:05 -07:00
admin 332708ad41 deploy.yml: rename env var alias 3DPRINT_DB_PASSWORD -> PRINT3D_DB_PASSWORD in provision-secrets. Shell/env var identifiers cannot start with a digit (POSIX); this invalid key was likely poisoning the entire environment block for this step, explaining why SWARM_MANAGER_IP read empty specifically in provision-secrets/deploy/verify (which all declare 20-30 secrets) while working fine in the isolated 2-secret debug step. The underlying Woodpecker secret name (3dprint_db_password) is unchanged -- only the YAML alias and its $-reference were renamed.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 12:12:39 -07:00
admin cfd735f14d vaultwarden: retry #5 -- real end-to-end test now that repo is public and pipeline bugs are fixed
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 11:57:30 -07:00
admin 01333893f1 deploy.yml: remove temporary debug-secrets step. Secret injection confirmed working (SSH_KEY=516 bytes, SWARM_MANAGER_IP=12 bytes) after repo was switched back to public. Root cause of the entire multi-day secret-provisioning saga: (1) CI_COMMIT_CHANGED_FILES renamed to CI_PIPELINE_FILES in Woodpecker 3.16, (2) --no-interpolate flag was misplaced, (3) dotfolders matched as bogus stack names, (4) private-repo flip broke Woodpecker's secret injection entirely. All four now fixed.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 11:57:02 -07:00
admin d89cfbf055 deploy.yml: add temporary debug-secrets step (prints byte length of SSH_KEY and SWARM_MANAGER_IP only, never values) to determine whether secret injection is failing repo-wide or just for swarm_manager_ip. Repo has been switched back to public.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 11:54:54 -07:00
admin 6fd3018a8c vaultwarden: retry #4 -- re-trigger after fixing --no-interpolate flag position and dotfolder false-match bugs
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 11:03:58 -07:00
admin c511b2b607 deploy.yml: fix two bugs surfaced now that CI_PIPELINE_FILES parsing actually works: (1) --no-interpolate was placed before the config subcommand instead of after, causing 'unknown flag' on any real folder-stack validate; (2) folder-detection regex matched dotfolders like .woodpecker as bogus stack names, causing SSH/SCP attempts with empty SWARM_MANAGER_IP. Also added explicit empty-var guards for SWARM_MANAGER_IP in provision-secrets/deploy/verify so future secret misconfig fails fast with a clear message instead of a cryptic ssh error.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 11:03:35 -07:00
admin a9297397c7 vaultwarden: retry #3 -- re-trigger secret provisioning now that CI_PIPELINE_FILES parsing is fixed
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 10:52:01 -07:00
admin 206f6ff103 deploy.yml: fix root cause - CI_COMMIT_CHANGED_FILES does not exist in Woodpecker 3.16; the correct variable is CI_PIPELINE_FILES (JSON array format, e.g. [\"a/b.yaml\",\"c.yaml\"]). Updated all 5 usages (validate, provision-secrets x2, deploy, verify, notify-success) to read CI_PIPELINE_FILES and strip [ ] \" before splitting on comma. Removes temporary debug-env step.
ci/woodpecker/push/deploy Pipeline failed
2026-07-27 10:51:37 -07:00
admin 2b6286a1c9 deploy.yml: add temporary debug-env step to diagnose CI_COMMIT_CHANGED_FILES not populating despite Woodpecker UI showing correct changed files. No logic changes to existing steps -- remove once root cause is found.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-27 10:31:10 -07:00
admin 9447b277a1 vaultwarden: remap DATABASE_URL secret to versioned name (vaultwarden_database_url_v2), same in-container target path. Fixes rotation being silently blocked by Swarm (cannot rm a secret referenced by a running service).
ci/woodpecker/push/deploy Pipeline was successful
2026-07-27 08:50:43 -07:00
admin 6ff6260b2f deploy.yml: fix vaultwarden secret provisioning (was bypassing shared helper, silently failing on rm-while-in-use). Provision under versioned name vaultwarden_database_url_v2; compose-side remap happens in a follow-up commit.
ci/woodpecker/push/deploy Pipeline was successful
2026-07-27 08:50:16 -07:00
admin 71cb6a7e81 vaultwarden: retry secret re-provision trigger (previous run had empty changed-files)
ci/woodpecker/push/deploy Pipeline was successful
2026-07-26 22:27:58 -07:00
admin 4a3eb44a62 vaultwarden: trigger secret re-provision (db -> postgresql alias) 2026-07-26 22:10:46 -07:00
admin 61b1a645bb uptime-kuma: set restart_policy.delay to 30s
Increases the delay before Swarm restarts a failed uptime-kuma task
from the default 5s to 30s. Applied live via `docker service update
--restart-delay 30s` on 2026-07-26; this persists it so it survives
the next stack deploy.

Note: this does not pin the task to a specific node or prevent
cross-node reschedule races against the CephFS-backed SQLite data
dir - it only slows the restart-after-failure loop. See prior
incident notes for the DB corruption root cause discussion.
2026-07-26 17:34:35 -07:00
admin 2b0ef411d1 chore(ai): sync ai.yaml with host (manual litellm v1.93.0 upgrade comment) 2026-07-25 22:20:28 -07:00
admin cc9880e09c chore(traefik): remove flat traefik.env, superseded by traefik/traefik.env 2026-07-25 22:20:27 -07:00
admin 1494224194 chore(traefik): remove flat traefik.yaml, superseded by traefik/traefik.yaml 2026-07-25 22:20:27 -07:00
admin d16203b873 chore(auth): remove flat auth.yaml, superseded by auth/auth.yaml 2026-07-25 22:20:26 -07:00
admin c2069e3e0d feat(traefik): migrate to folder-based stack layout 2026-07-25 22:19:26 -07:00
admin 6caa7f0a33 feat(traefik): migrate to folder-based stack layout 2026-07-25 22:19:25 -07:00
admin 7ccff906fd feat(auth): migrate to folder-based stack layout 2026-07-25 22:19:24 -07:00
admin 68f89ef139 chore(ci): remove root .woodpecker.yml, superseded by .woodpecker/deploy.yml
Woodpecker ignores this file entirely once .woodpecker/ folder exists
(config resolution: .woodpecker/ > .woodpecker.yaml > .woodpecker.yml).
Content preserved verbatim in .woodpecker/deploy.yml.
2026-07-25 22:00:57 -07:00
admin 270489b207 fix(ci): move .woodpecker.yml -> .woodpecker/deploy.yml
Root cause of Woodpecker push pipelines silently no-op'ing since
2026-07-18: Woodpecker's config resolution is
  .woodpecker/*.{yaml,yml} -> .woodpecker.yaml -> .woodpecker.yml
Once .woodpecker/renovate.yml existed, the root .woodpecker.yml was
entirely ignored. Every push webhook hit renovate.yml's `when: event:
cron` filter, matched zero steps, and Woodpecker logged
"ignoring hook: 'when' filters filtered out all steps" instead of
running deploy.

Moving the push pipeline into .woodpecker/deploy.yml restores it as
an independent workflow alongside renovate.yml -- each file's own
`when:` filter now applies correctly (push -> deploy.yml, cron ->
renovate.yml).
2026-07-25 22:00:49 -07:00
admin cc5adc3312 feat(mcpo): sync config.json to Gitea as source of truth
Adds powerautomate entry with Azure app registration credentials.
Auth dir (/volume1/docker/mcpo/powerautomate-auth/) and config stub
already exist on host from prior session.

NOTE: powerautomate-mcp package not yet on npm - npx will fail until
published. Tracked here for GitOps pipeline; will update command when
package is available or we switch to build-from-source approach.
2026-07-25 15:10:07 -07:00
admin 963a52d665 litellm: upgrade v1.92.1 -> v1.93.0 to fix Bedrock parallel tool call truncation bug 2026-07-24 22:16:39 -07:00
admin 2a1feee03e feat(mcpo): sync config.json from host (real PA_MCP_CLIENT_ID + TENANT_ID) 2026-07-24 22:06:24 -07:00
admin 8fde5266ea chore(ai): remove flat ai.yaml, superseded by ai/ai.yaml 2026-07-24 22:02:22 -07:00
admin 92abfe295f feat(ai): migrate to folder-based stack layout 2026-07-24 22:01:45 -07:00
admin 3da6a89919 feat(woodpecker): add PA_MCP_CLIENT_ID secret provisioning for ai stack 2026-07-19 14:23:22 -07:00
admin 0030d92ca8 chore(mcpo): sync config.json from live disk - adds powerautomate entry 2026-07-19 14:20:27 -07:00
admin 0f7f00404d feat(mcpo): add config.json to source control with powerautomate, ms365 fixes
- Pin ms365 to 0.129.0 with --discovery flag (fixes MCPO schema parse error)
- Add powerautomate entry (PA_MCP_CLIENT_ID placeholder - needs Azure app reg)
- Update authentik token (was expired)
- teams-mcp-auth volume now source of truth for ms365/teams auth cache
- powerautomate-auth volume wired for future PA auth cache

NOTE: config.json was previously managed manually on disk only.
This commit brings it under GitOps control.
2026-07-19 14:16:40 -07:00
admin 815d953cd7 chore(renovate): remove unused TEAMS_WEBHOOK env - notifications via Gitea webhook 2026-07-19 07:00:37 -07:00
admin 310c68376a chore(renovate): revert postUpgradeTasks - notifications via Gitea webhook instead 2026-07-19 07:00:17 -07:00
admin 43f3fdce94 chore(renovate): add postUpgradeTasks Teams notification via Power Automate webhook 2026-07-19 06:59:49 -07:00
admin d9a6d217bc chore(renovate): pass TEAMS_WEBHOOK secret into renovate container 2026-07-19 06:59:28 -07:00
admin 088d884a88 chore(renovate): add Teams webhook notification via Power Automate 2026-07-19 06:59:02 -07:00
admin 96783bce8c chore(renovate): fix config warnings - remove global-only options, disable woodpecker manager 2026-07-18 22:48:22 -07:00
admin 8185fb5842 feat: add woodpecker cron pipeline for renovate (v3 syntax) 2026-07-18 22:05:19 -07:00
admin fe6c95f54a feat: add renovate.json configuration 2026-07-18 22:05:01 -07:00