Skip to main content

Receipt — adoption: taking custody of a live estate without recreating a pod

Date 2026-08-25 · Cluster throwaway LKE 647376, namespace vexa-prod-sim, destroyed at the end of this run · Channel vexa-internal · Chart vexa-platform 0.1.20260825 This is the second of two proofs. The first — clean pull — asks whether the channel can produce production from nothing. This one asks whether a production that already exists can be handed over to the channel without going down. They are not the same claim and neither substitutes for the other. Adoption proves zero-downtime custody transfer of a live estate; it proves nothing about deliverability, because the images are already on the nodes and the objects already exist. Deliverability is Proof 1’s job, and Proof 1 is where the findings are. Production was never touched. This ran against a Helm-installed simulation in a throwaway namespace.

Setup — what was adopted

helm install vexa-platform from the channel chart, release name vexa-platform (the live production release name, not the kit’s old hardcoded vexa), into vexa-prod-sim, with dummy secrets shaped per docs/channel-secret-mapping.md and the database pointed at an in-cluster double per the validation contract. Settled state before adoption: 19 pods, 18 Running or Succeeded, 3 total container restarts (all on one billing worker, from a real guard — see below).

The headline number

Zero pods were recreated. 18 of 18 kept the same pod UID across the adoption sync. A same-name pod with a same UID is the same running process: it was never deleted, never rescheduled, never restarted from scratch. This is the number that matters, because the failure mode adoption is guarding against — Argo’s default label resource tracking trying to write app.kubernetes.io/instance into an immutable Deployment spec.selector.matchLabels — does not produce an error message. It produces a permanently OutOfSync Application whose only exits are recreating the workload or unwinding the install. And the decisive corroboration, which does not depend on reading pod status at all:
Not one Deployment produced a second ReplicaSet. A ReplicaSet is created whenever a pod template changes, so one-per-Deployment is proof that the adoption changed no pod spec anywhere in the estate. There is nothing to interpret in that number. The kit change that makes this true is in kit/install.sh: application.resourceTrackingMethod=annotation is set on argocd-cm before any Application exists, so the tracking id lands in a mutable annotation rather than in the selector. Verified live:

The diff before sync

Argo compared the published chart against the running estate before being allowed to change anything:
110 of 115 objects needed no change at all. The 5 that differed:
The delta, in full, for one of them — and it is representative:
Ownership metadata only. Nothing in any spec. These five are the objects whose templates omit the chart’s standard label block, which is why they show a difference where the other 110 do not — Helm’s ownership annotations are present on the live object and absent from the render. Adoption’s entire effect on this estate was to change who owns five objects.

What the sync did

Restart counts moved on three pods inside the sync window (admin-api 0→2, billing-auto-topup-worker 0→3, billing-provider-worker 3→6) and then stopped: re-measured 100 seconds later with no Argo activity, every count was stable except the billing-provider-worker, which is in an independent CrashLoopBackOff. I am not going to claim those three were unrelated to the adoption, because I cannot prove it. What I can prove is that no pod was recreated and no pod template changed, so whatever those restarts were, they were containers restarting in place, not workloads being replaced. The billing-provider-worker was already restarting before adoption began, for a reason unrelated to it:
That is a real custody guard refusing a test-mode Stripe key, and it is doing exactly what it should.

Pull upgrade and pin move — the prod ceremony, timed

The ceremony production will use is: publish → staging proves → move the pin. Rollback is move the pin back. Both directions were rehearsed against the adopted estate. Across both moves, 17 of 17 pods kept the same UID. One restart-count change, on the billing-auto-topup worker that was already unstable. Rollback is not a special procedure, a script, or a runbook. It is the same one-field edit as the forward move, and it was measurably faster.

The exact prod ceremony, now validated

Every step below was executed on the throwaway:
  1. Publish the estate to the channel — chart, entry, signatures, mirrored images.
  2. Sign every image into the channel’s signature repo. Do not skip this: Kyverno was observed denying 12 workloads with no signatures found before signing and admitting all of them after. The gate was seen failing closed before it was seen passing.
  3. Install the kit with --chart-name vexa-platform --release-name vexa-platform. The release name must match the existing release; the kit’s old hardcoded vexa would have installed a second copy alongside production rather than adopting it.
  4. Confirm resourceTrackingMethod=annotation on argocd-cm before any Application exists.
  5. Create the Application, do not sync. Read the diff.
  6. Reconcile until the delta is ownership metadata only. This is a gate, not a formality: a spec difference at this point means the channel does not hold what production runs, and syncing would change production.
  7. Sync. Record pod UIDs and ReplicaSet counts before and after.
  8. Assert zero recreations — same UIDs, one ReplicaSet per Deployment.
  9. Pin move forward, then back, before trusting the mechanism with a real release.

What still differs from real production

Stated plainly, because a rehearsal that does not name its gaps is a rehearsal that will be over-trusted. The most important gap is the last two rows. This proves adoption for the vexa-platform release. It does not prove adoption for the monitoring namespace’s three releases, which carry their own ownership metadata and their own Helm history, and which include a Prometheus custom resource with an operator behind it.

Custody

vexa-production and monitoring on LKE 590708 were read-only throughout, and vexa-staging was not touched. The throwaway cluster and its volumes were deleted at the end of this run.