Learn: Delivery — reference
A lookup for the delivery mechanism. For the narrative, see the orientation.
The mechanism, end to end
Section titled “The mechanism, end to end”| Piece | What it is | Where |
|---|---|---|
Release record |
the deployed digest(s) for one Environment — the source of truth for “what runs where” | gitops/releases/<team>/<product>/<stage>.yaml |
| Per-Product ApplicationSet | fans out over a Product’s Releases → one ArgoCD Application per Release; injects the digest |
infra/modules/argocd-apps/delivery.tf |
| Auto-promoter | cron reconciler that climbs the stage ladder, health-gated | .github/workflows/auto-promote.yml → reconcile.sh |
| gitops Gate | validates + auto-merges Release PRs (≤ staging); holds prod for a human | .github/workflows/gitops-gate.yml |
| Argo Rollout | the workload primitive — weighted-canary rollout of each digest | app repo k8s/overlays/<stage> |
The Release record
Section titled “The Release record”apiVersion: platform.refplat.org/v1beta1kind: Releasemetadata: name: alpha-shop-devspec: environmentRef: alpha-shop-dev # <team>-<product>[-<customer>]-<stage> services: storefront: digest: sha256:f6b37d… # the deployed image digest for this service, this stage- One file per Environment/stage. A Service with no entry isn’t deployed there.
- The digest is what deploys, not a tag — immutable and content-addressed (see Life of a Deployment for why).
- Keyed on the Release, not the Environment, so a Product can deliver to more than one stage without a merge-key collision.
Delivery — the ApplicationSet
Section titled “Delivery — the ApplicationSet”- One ApplicationSet per Product. A git-files generator fans out over
gitops/releases/<t>/<p>/*.yamlinto oneApplicationper Release. An Environment with no Release yet generates no Application — so no doomed:placeholdersync. - The template derives stage (and optional customer) from
spec.environmentRef, pointssource.pathat the app repo’sk8s/overlays/<stage>, and atemplatePatchinjects the per-Service digest as a kustomize image override. - Sync policy is
automated { selfHeal = true, prune = true }plus ServerSideApply. Self-heal reverts drift; prune deletes what git no longer declares. - Cross-account delivery: one ArgoCD on the hub delivers to environment spoke clusters across the
account boundary, one spoke per account. Only the
preprodspoke exists today (argocd-clustersregisters it); the dedicated prod-account spoke isn’t built yet, so every stage includingprodlands as a namespace on preprod for now. Each spoke is reached via a labeled clusterSecretcarrying AWS IAM auth — the application-controller does STS AssumeRole plus an EKS token per target. The mechanism is generic over registered spokes. - Agents deliver to the hub via a separate platform-agent ApplicationSet (
agents.tf), not to the environment spokes.
Promotion — how a digest climbs
Section titled “Promotion — how a digest climbs”Three producers, one output: an asanexample-promote[bot] Release PR that the Gate validates.
- On-demand — Backstage. The Request Promotion scaffolder template resolves the source stage’s digest and opens the target Release PR.
- On-demand — app repo.
promote.yml(a thin caller of the sharedtrusted-ciworkflow) viaworkflow_dispatch, taking afrom_stage; resolves the source digest post-clone. - Automatic ≤ staging —
reconcile.sh(cron). Walks adjacent pairsdev→test→uat→staging(prod excluded). Per Service it skips a rung already carrying the digest (idempotent), requires the upper Environment to declare that Service, and gates on the lower stage’s Application beingSynced + Healthy. The digest climbs one rung per run, baking at each stage.
The ladder: dev → test → uat → staging promotes automatically (health-gated); prod is gated by a
release-approver. Prod Release PRs never auto-merge.
Progressive delivery — the Rollout
Section titled “Progressive delivery — the Rollout”- Every environment workload is a direct-
spec.templateArgo Rollout, not a Deployment, on every stage — so prod is never the first place a Rollout runs. - Traffic shaping: the Gateway-API traffic-router plugin (
argoproj-labs/gatewayAPI) does weighted HTTPRoute canary on the Cilium Gateway — Rollouts edits the HTTPRoute weights, the data plane routes by weight (see Life of a Request). - Admission awareness — a real gotcha. A
Rolloutisn’t aDeployment, so the workload-level availability policies (replica-floor, PDB-generate, topology-spread) only match it whenenable_rollout_kind = trueis set per cluster (default off — a Kyverno rule can’t name a CRD that’s absent, so it’s enabled only after theargo-rolloutsunit is applied there). Pod-level policies (securityContext, preStop drain, cosign-verify) apply to Rollout pods automatically via ReplicaSet autogen. So a Rollout is admission-aware, but the availability mutations are an explicit opt-in, not free. - Weighted-canary shifting drives each step, and lower stages auto-promote through the steps (dogfooding);
prod pauses on a metric-gated
AnalysisTemplate(query Mimir → auto-rollback on an SLO breach).
Gotchas
Section titled “Gotchas”OutOfSyncwith an empty diff. A server-side-appliedRollout/HTTPRoutecarries two field managers (argocd-controller + the rollouts-controller); ArgoCD’s default client-side diff mis-reads it as drift. Fix with server-side diff (controller.diff.server.side=true) —ignoreDifferencescan’t fix it.- A Rollout’s image can silently stay stale while ArgoCD reports success.
Rolloutis a CRD without a registered Kubernetes scheme, so a sync that has to reconcile anignoreDifferencesrule touching an array field on it (containers[], not a scalar likereplicas) can silently drop the real change instead of applying it — the sync still logs success.argocd-controller’s ownmanagedFieldstimestamp is the tell: it won’t have moved since before your “successful” sync. - A stuck promotion is usually correct. The auto-promoter won’t advance a rung while the lower stage
isn’t
Synced + Healthy. That’s the health gate, not a bug — fix the unhealthy lower stage. - Prod won’t move on its own — by design. No cron promotes into prod; it always waits on the release-approver.
- The digest lives in the platform repo, not your app repo. Your
mainstays protected and CI-commit-free.
Glossary
Section titled “Glossary”Releaserecord — a CR naming the deployed digest(s) for one Environment; the delivery source of truth.- ApplicationSet — an ArgoCD controller that generates Applications from a data source (here, the Product’s Release files).
- Sync / self-heal / prune — ArgoCD reconciling the cluster to git: apply desired, revert drift, delete the undeclared.
- Rollout — Argo Rollouts’ Deployment-replacement that adds canary/blue-green + analysis.
- Canary — routing a slice of traffic to the new version first (weighted HTTPRoute here).
- The ladder — the ordered stages
dev → test → uat → staging → proda digest climbs. - release-approver — the human role that must approve a prod Release PR.
Go deeper
Section titled “Go deeper”- Promotion & Release.
- The
argocd-app-deliveryhouse skill (ApplicationSets, PR previews, Release-keyed delivery). - Substrate: ArgoCD · ApplicationSets · Argo Rollouts · GitOps principles.