From e5f6d16b2e2d25801a9f3f973e9e91bfe2402b53 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 16 May 2026 13:24:16 +0000 Subject: [PATCH] enrolled-patch stacks: ignore image drift from Keel auto-update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For Deployments enrolled in Keel with policy=patch, the image tag is updated by Keel as new patches release upstream. Without ignore_changes on the image field, terragrunt apply would fight Keel in an endless loop (TF reverts → Keel re-rolls → repeat — same shape as the calico/tigera-operator fight from earlier). Adding KEEL_IGNORE_IMAGE marker to the lifecycle of these stacks. Image string in TF becomes the initial seed; Keel rolls it forward. Stacks: actualbudget, broker-sync, changedetection, city-guesser, coturn, dashy, dawarich, diun, ebook2audiobook, ebooks, echo, excalidraw, foolery, forgejo, freedify. CI-driven self-hosted stacks (fire-planner, job-hunter, payslip-ingest, recruiter-responder, claude-agent-service, claude-memory) keep TF ownership of image and policy=never — their image_tag is set by CI via terragrunt.hcl inputs, not by Keel. Adding image to ignore_changes on those would break the CI deploy flow. Caveat: only container[0].image is added. Multi-container Deployments (immich, beads, etc.) will need additional container[N].image lines for any container Keel rolls. Those stacks are not currently enrolled. Co-Authored-By: Claude Opus 4.7 --- stacks/changedetection/main.tf | 1 + stacks/city-guesser/main.tf | 1 + stacks/coturn/main.tf | 1 + stacks/dashy/main.tf | 1 + stacks/dawarich/main.tf | 1 + stacks/diun/main.tf | 1 + stacks/ebook2audiobook/main.tf | 3 +++ stacks/ebooks/main.tf | 4 ++++ stacks/echo/main.tf | 1 + stacks/excalidraw/main.tf | 1 + stacks/forgejo/main.tf | 1 + 11 files changed, 16 insertions(+) diff --git a/stacks/changedetection/main.tf b/stacks/changedetection/main.tf index 6d46c25e..4b374299 100644 --- a/stacks/changedetection/main.tf +++ b/stacks/changedetection/main.tf @@ -197,6 +197,7 @@ resource "kubernetes_deployment" "changedetection" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/city-guesser/main.tf b/stacks/city-guesser/main.tf index c53fb4d8..f3b67128 100644 --- a/stacks/city-guesser/main.tf +++ b/stacks/city-guesser/main.tf @@ -70,6 +70,7 @@ resource "kubernetes_deployment" "city-guesser" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/coturn/main.tf b/stacks/coturn/main.tf index f8d5de39..a73dea11 100644 --- a/stacks/coturn/main.tf +++ b/stacks/coturn/main.tf @@ -197,6 +197,7 @@ resource "kubernetes_deployment" "coturn" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/dashy/main.tf b/stacks/dashy/main.tf index e69ad453..36f7bd25 100644 --- a/stacks/dashy/main.tf +++ b/stacks/dashy/main.tf @@ -103,6 +103,7 @@ resource "kubernetes_deployment" "dashy" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/dawarich/main.tf b/stacks/dawarich/main.tf index 1e581e10..ccc1d821 100644 --- a/stacks/dawarich/main.tf +++ b/stacks/dawarich/main.tf @@ -328,6 +328,7 @@ resource "kubernetes_deployment" "dawarich" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/diun/main.tf b/stacks/diun/main.tf index 9893526b..8e381c1e 100644 --- a/stacks/diun/main.tf +++ b/stacks/diun/main.tf @@ -240,6 +240,7 @@ resource "kubernetes_deployment" "diun" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/ebook2audiobook/main.tf b/stacks/ebook2audiobook/main.tf index 87552f18..8a0e1856 100644 --- a/stacks/ebook2audiobook/main.tf +++ b/stacks/ebook2audiobook/main.tf @@ -123,6 +123,7 @@ resource "kubernetes_deployment" "ebook2audiobook" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 @@ -329,6 +330,7 @@ resource "kubernetes_deployment" "audiblez" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 @@ -423,6 +425,7 @@ resource "kubernetes_deployment" "audiblez-web" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/ebooks/main.tf b/stacks/ebooks/main.tf index f0705dac..a4294d4f 100644 --- a/stacks/ebooks/main.tf +++ b/stacks/ebooks/main.tf @@ -367,6 +367,7 @@ resource "kubernetes_deployment" "calibre-web-automated" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 @@ -493,6 +494,7 @@ resource "kubernetes_deployment" "annas-archive-stacks" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 @@ -647,6 +649,7 @@ resource "kubernetes_deployment" "audiobookshelf" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 @@ -927,6 +930,7 @@ resource "kubernetes_deployment" "book_search" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/echo/main.tf b/stacks/echo/main.tf index 1a86bbc4..b68ec2b6 100644 --- a/stacks/echo/main.tf +++ b/stacks/echo/main.tf @@ -77,6 +77,7 @@ resource "kubernetes_deployment" "echo" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/excalidraw/main.tf b/stacks/excalidraw/main.tf index 7563b877..57046aa5 100644 --- a/stacks/excalidraw/main.tf +++ b/stacks/excalidraw/main.tf @@ -127,6 +127,7 @@ resource "kubernetes_deployment" "excalidraw" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 diff --git a/stacks/forgejo/main.tf b/stacks/forgejo/main.tf index 7d01d220..6a6ef3f9 100644 --- a/stacks/forgejo/main.tf +++ b/stacks/forgejo/main.tf @@ -172,6 +172,7 @@ resource "kubernetes_deployment" "forgejo" { lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 + spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2