The golden path (Kubernetes)
The flagship flow: a tenant-level kind cluster provisioned by Terraform, with ArgoCD, Argo Rollouts and Gitea inside it. Deploys are GitOps for real: the CLI pushes manifests to the in-cluster Gitea repo, ArgoCD syncs them and the Rollout canaries the new version.
Prerequisites
Section titled “Prerequisites”brew install kind kubectl hashicorp/tap/terraform argoproj/tap/kubectl-argo-rolloutsnpm install -g @blissful-infra/cliPlus Docker Desktop, running. None of this is needed for the compose runtime, only for this path.
The path
Section titled “The path”1. Tenant and cluster
Section titled “1. Tenant and cluster”blissful-infra tenant create acmeblissful-infra cluster upBudget 3-5 minutes for the first cluster up: Terraform downloads providers and Helm pulls the ArgoCD, Argo Rollouts and Gitea charts. It prints the ArgoCD and Gitea URLs and credentials when it finishes.
2. A kubernetes-runtime project and a service
Section titled “2. A kubernetes-runtime project and a service”blissful-infra project create shop --runtime kubernetesblissful-infra service add orders --type backend--runtime kubernetes is the consequential flag. It makes the project a namespace on the cluster and its services Argo Rollouts, rather than plain containers. It cannot be changed later without recreating the project.
3. Deploy
Section titled “3. Deploy”blissful-infra deploy ordersFive things happen:
- Build the service image
- kind load it onto the cluster node: kind-loaded images never pull
- Render the Rollout, canary/stable Services and ConfigMap
- Commit and push to the tenant’s Gitea repo
- ArgoCD syncs the commit, and Argo Rollouts starts the canary
4. Watch it
Section titled “4. Watch it”blissful-infra canary status orders # 10% canary / 90% stable, step 1/7blissful-infra dashboard up # Environments tabThe dashboard’s Environments tab shows the rollout live: a weight bar, a step counter and Promote / Promote Full / Abort buttons.
5. Promote
Section titled “5. Promote”The rollout walks four weights on its own, pausing between them. Promote early when you have seen enough:
blissful-infra canary promote orders # next stepblissful-infra canary promote orders --full # straight to 100%| Step | Weight | Pause |
|---|---|---|
| 1 | 10% | 2m |
| 2 | 25% | 2m |
| 3 | 50% | 5m |
| 4 | 100% | - |
6. Ship a change
Section titled “6. Ship a change”# edit ~/.blissful-infra/tenants/acme/projects/shop/services/orders/src/...blissful-infra deploy ordersAnother commit, another sync, another canary.
7. Roll back
Section titled “7. Roll back”blissful-infra rollback ordersThis reverts the deploy commit in the gitops repo and lets ArgoCD converge back. That matters: ArgoCD’s selfHeal continuously reconciles the cluster against git, so an imperative rollback would be undone on the next reconcile. Reverting the commit is the only rollback that sticks.
8. Tear down
Section titled “8. Tear down”blissful-infra cluster down # terraform destroyWhy this shape
Section titled “Why this shape”Gitea runs inside the cluster. The deploy loop could have been “run kubectl apply”. Instead the CLI commits rendered manifests to a real git server and lets ArgoCD pull them, which gives the loop the same shape as a production GitOps setup: every deploy is a commit, and a rollback is a revert against the repo ArgoCD treats as the source of truth. Open Gitea at the URL cluster up printed and you can read the whole deploy history as commits.
Argo Rollouts, not Deployments. A Kubernetes Deployment gives you a rolling update, which is not a canary. There is no point at which a known fraction of traffic is on the new version and you get to decide. Rollouts give you weights, pauses and an explicit promote.
Current limits
Section titled “Current limits”Two, both deliberate scoping decisions for the first slice:
No in-cluster Postgres. Kubernetes-runtime services scaffold without a database binding. The tenant’s Postgres runs on the compose side and the cluster cannot reach it.
Canary analysis is pause-based, not metric-driven. Argo Rollouts can query Prometheus and promote or abort automatically, but that needs an in-cluster Prometheus, which is not there yet. So the rollout uses timed pauses and leaves the call to you.
Both are tracked as follow-ups in ADR-0020.
Troubleshooting
Section titled “Troubleshooting”cluster up hangs or times out. Usually Docker running low on resources: kind needs a few GB. Check docker stats and Docker Desktop’s memory allocation.
The rollout never leaves step 1. Check the pods are actually passing their readiness probe: blissful-infra cluster status. The Spring Boot template probes /actuator/health.
ArgoCD shows OutOfSync after a rollback. Expected briefly while it converges. If it persists, the revert commit may not have pushed. Check the Gitea repo directly.