Promote Image¶
Deploy a new image tag to a tenant environment.
Purpose¶
Deploy a specific image tag to one or all tenant environments by triggering do-deploy.yml workflow dispatch.
When to use¶
- Rolling out a new release to staging or production
- Re-deploying an older tag (rollback)
- Promoting a staging-validated tag to production
Steps¶
Deploy latest release to a tenant¶
- Go to
unipuka/soa-> Actions ->do-deploy.yml-> Run workflow - Set
tag= desired image tag (e.g.v1.2.3or git SHA) - Set
tenant=oep-stg/mansety-prd/us-prd - For prod tenants: reviewer approval required before workflow proceeds
- Workflow commits
chore(deploy): bump <tenant> to <tag>tounipuka-infra-ops/master - Argo CD detects change within 3 min, runs PreSync migrate Job, then rolls Deployments
Re-deploy an older tag (rollback)¶
If the image tag is still in DOCR (within 30-tag retention window):
# Dispatch with the older tag
gh workflow run do-deploy.yml -R unipuka/soa \
-f tag=v1.1.0 -f tenant=oep-stg
If image was GC'd from DOCR (beyond 30-tag window), rebuild first:
# Rebuild image from historical commit
gh workflow run do-build.yml -R unipuka/soa \
-f ref=<git-sha-or-tag>
# Then deploy as normal
Chart versioning lockstep (BD-5.3 contract)¶
Every do-deploy.yml dispatch MUST bump three keys atomically in one commit:
apps/laravel/values-<tenant>.yaml:image.tag-> new semver (e.g.v1.2.3)apps/laravel/Chart.yaml:version-> bump (e.g.0.1.0->0.1.1)apps/laravel/Chart.yaml:appVersion-> same semver as image.tag
Without all 3 bumps, ct lint check-version-increment: true blocks the PR (helm-lint CI turns red).
This also gives Argo CD a deterministic chart version per deploy (visible in Argo UI revision history).
# Example bump for oep-stg deploy of v1.2.3
NEW_TAG=v1.2.3
yq -i ".image.tag = \"$NEW_TAG\"" apps/laravel/values-oep-stg.yaml
yq -i ".version = \"0.1.1\"" apps/laravel/Chart.yaml
yq -i ".appVersion = \"$NEW_TAG\"" apps/laravel/Chart.yaml
git add apps/laravel/
git commit -m "chore(deploy): bump oep-stg to $NEW_TAG"
DOCR capacity¶
Basic tier (5 GiB / $5/mo) chosen over Starter (500 MiB hard cap). Single shared oep/api repo for all tenants (image identical; only Doppler env differs). Image size ~363 MB. Basic gives ~13 image slots raw, but layer deduplication makes retention much higher in practice. Two-layer GC (per-push tag prune + weekly untagged-manifest sweep) keeps storage well under 5 GiB ($0.02/GiB overage if exceeded).
Registry tier change is a registry.tf one-liner (subscription_tier_slug = "basic"). Applied in BD-4.0.
DOCR retention¶
Two-layer retention strategy (BD-5.5):
| Layer | Trigger | What it does |
|---|---|---|
| Per-push tag prune | Every master push (prune job in do-build.yml) |
Deletes oldest tags beyond newest-30 count. Fast, runs immediately after each release. |
| Weekly backstop prune | Sunday 03:00 UTC (docr-gc.yml cron) + workflow_dispatch |
Same tag-count prune as per-push, but as a weekly safety net. |
| DOCR automated GC | Daily, platform-managed by DigitalOcean | Sweeps untagged manifests and unreferenced blobs automatically in our current registry configuration; cannot be disabled or triggered manually here (HTTP 412 if attempted while automated GC is enabled in this registry). |
Rules:
latesttag is always excluded from deletion.- Tags sorted by
UpdatedAtdescending; oldest beyond count-30 are deleted. - After 30 tagged images exist, each new push removes the oldest one automatically.
- If image was pruned (beyond 30-tag window), rebuild from the original commit:
Verify tag count after a prune run:
Rollback¶
Git revert the deploy commit in unipuka-infra-ops/master - Argo CD prune + selfHeal will re-sync the previous tag.
V&V¶
argocd app get <tenant>-laravelshows new image tag andSynced/Healthykubectl get pods -n <tenant>allRunningwith new imagecurl https://<hostname>/healthreturns 200