Skip to content

Per-tenant loops

Multi-tenant SaaS, per-region deployments, per-customer demo instances — whenever you want the same shape repeated with different parameters, use a for block.

values/tenants.json
{
"tenants": [
{ "name": "acme", "domain": "acme.example.com", "plan": "enterprise" },
{ "name": "demo", "domain": "demo.example.com", "plan": "starter" },
{ "name": "beta", "domain": "beta.example.com", "plan": "starter" }
]
}
vars.kdef
variable "tenants" {
type = "any"
default = []
}
booking.kdef
for "tenant" "var.tenants" {
deployment "booking" {
name = "booking-${tenant.name}"
namespace = "production"
container "booking" {
image = image("booking")
env {
TENANT_ID = tenant.name
TENANT_PLAN = tenant.plan
}
resources {
cpu = tenant.plan == "enterprise" ? "500m..2000m" : "100m..500m"
memory = tenant.plan == "enterprise" ? "512Mi..2Gi" : "128Mi..512Mi"
}
}
scale {
replicas = tenant.plan == "enterprise" ? 3 : 1
}
service {
port "80" "http" {}
}
ingress {
host = tenant.domain
tls = true
}
}
}
Terminal window
kdef render --dir k8s/ --values values/tenants.json

Output: three full app stacks — each with its own Deployment, Service, Ingress, Certificate — named booking-acme, booking-demo, booking-beta.

Edit values/tenants.json, add an entry, run kdef diff — only the new tenant’s resources show up in the diff.

  • Keep values/ files in git. They’re not secrets.
  • Combine for with if blocks if some tenants need extra resources:
    for "tenant" "var.tenants" {
    if {
    condition = tenant.plan == "enterprise"
    cronjob "nightly-report-${tenant.name}" {
    schedule = "0 2 * * *"
    image = image("reporter")
    env { TENANT_ID = tenant.name }
    }
    }
    }
  • For many tenants (dozens+), render with kdef render > out.yaml once and inspect to make sure the output is what you expect before applying.