How Kubo reconciles a Stack
The flow
A Stack manifest lands in the cluster (from kubectl, ArgoCD, or the kubify-website SaaS portal). The kubo operator watches every Stack CR, pulls its OCI bundle, and installs components as Helm releases in dependency order — propagating secrets and reconciling Istio routing along the way.
flowchart TD
WEB["kubify-website (SaaS portal)
Customer signs up or deploys a stack"]
subgraph K8S["Kubernetes cluster (AKS)"]
direction TB
subgraph SYS["kubo-system namespace"]
OP["kubo operator
(controller-manager)"]
CRDS["Stack CRD · StackDefinition CRD"]
PULL["acr-pull-secret · stack-a-secrets"]
end
subgraph TEN["stack-a namespace (tenant)"]
STACK["Stack CR (stack-a)"]
HELM["Helm releases
(one per declared component)"]
SECRETS["Propagated secrets
(from kubo-system)"]
VS["Istio VirtualService
(routing)"]
end
end
OCI["OCI registries (ghcr.io)
product bundle: charts + images + values, versioned"]
WEB -- "kubectl apply / ArgoCD Stack manifest" --> STACK
STACK -- "watched by" --> OP
OP -- "reads CRD schema" --> CRDS
OP -- "pulls OCI artifacts" --> OCI
OP -- "Helm install/upgrade" --> HELM
OP -- "copies secrets (secretsRef)" --> SECRETS
OP -- "reconciles" --> VS
PULL -- "secretsRef propagation" --> SECRETS
The reconcile loop
Every change to a Stack — a new bundle tag, a values override, a pause, or a manual reconcile trigger — re-enters the same loop. Components are planned into dependency waves, deployed in order, and status rolls back up into the Stack CR.
flowchart LR
EVT["Stack event
(spec change, bundle tag,
reconcile trigger, un-pause)"]
PLAN["Plan dependency waves
(topological order over
dependsOn / dependsOnReady)"]
DEPLOY{"Deployment mode"}
DIRECT["Direct
embedded Helm engine:
install / upgrade in order"]
FLUX["Flux
compile to HelmReleases,
helm-controller deploys"]
STATUS["Roll up per-component phase,
revision & conditions
into Stack status"]
PRUNE["Prune releases dropped
from the spec"]
EVT --> PLAN --> DEPLOY
DEPLOY -- "Direct" --> DIRECT
DEPLOY -- "Flux" --> FLUX
DIRECT --> STATUS
FLUX --> STATUS
STATUS --> PRUNE --> EVT
Shared operators, tenant workloads
Cluster-scoped operators (Vault, Spark, Istio, …) install once into a shared operators namespace and watch every tenant namespace. Tenant charts never duplicate them — a tenant Stack declares which operators it needs, and the operator wires credentials, secrets, and routing across namespace boundaries.
flowchart TD
subgraph OPS["operators namespace (shared, one per cluster)"]
VAULT["Vault"]
SPARK["Spark"]
ISTIO["Istio"]
end
subgraph TA["tenant namespace"]
STACK["Stack
operators: vault ✓ istio ✓"]
SECRETS["tenant secrets
(seeded from kubo-system)"]
APPS["app workloads"]
end
STACK -- "enables" --> VAULT
STACK -- "enables" --> ISTIO
VAULT -- "watches tenant ns" --> SECRETS
ISTIO -- "watches tenant ns" --> APPS
Deployment modes
Direct
The operator's embedded Helm engine installs and upgrades releases itself, in dependency order. No extra moving parts — simplest path, full status detail per component.
spec.mode: Direct
Flux
The operator compiles each component into a Flux HelmRelease and hands execution to helm-controller. Choose it when the cluster already standardizes on Flux for GitOps continuity.
spec.mode: Flux
Where everything lives
| Location | Contents | Role |
|---|---|---|
| kubo-system | operator (controller-manager), Stack + StackDefinition CRDs, seeded secrets, pull secrets | control plane — watches every Stack, propagates credentials |
| operators ns | Vault, Spark, Istio, cert-manager, … | cluster-scoped services, installed once, watching all tenants |
| tenant ns | Stack CR, Helm releases, propagated secrets, Istio VirtualServices | the running product — one namespace per tenant |
| OCI registry | product bundles (charts + images + values, versioned) | the delivery artifact — portable across clusters and orgs |
Bundle once, deploy anywhere
Bundles are plain OCI artifacts on any registry — ghcr.io included. Because a Stack references a bundle by URL, the same artifact deploys to any cluster: dev, staging, or a customer's AKS estate. Composition is by reference, so products can build on shared bundles without vendoring each other's charts.
Shared operators
Vault, Spark, Istio and friends install once per cluster into the operators namespace and watch every tenant namespace. Tenants get the operators they declare — never duplicate controllers, never cross-tenant coupling.