Why kdef
You have two options for templating Kubernetes manifests, and neither feels right.
Kustomize has no variables and no loops. You end up maintaining a tree of base/ and overlays/, duplicating blocks of YAML, and writing strategic-merge patches that only make sense if you can hold the whole tree in your head.
Helm has variables and loops, but it does them with Go templates wrapped around YAML. The result is a language where a missing space or a forgotten toYaml | nindent 4 produces an invalid manifest at deploy time. You can’t read a Helm chart. You run helm template and read the output.
kdef is what sits between them.
What kdef is
Section titled “What kdef is”- A language — HCL syntax. Typed variables. Native
forloops. Conditionals that aren’t strings. - A compiler —
kdef renderproduces plain Kubernetes YAML. Nothing hidden.kdef diffshows exactly what will change in the cluster. - A deployment tool —
kdef applydoes server-side apply.kdef importturns a live namespace into.kdeffiles.
kdef is purpose-built for Kubernetes: a deployment block generates a Deployment, Service, Ingress, Certificate, and HPA. You write your app in one block instead of five.
What kdef gives you
Section titled “What kdef gives you”Typed variables
Section titled “Typed variables”variable "environment" { type = "enum[staging, production]" default = "staging"}
variable "replicas" { type = "number" default = 1}Typo in the value? Caught at kdef validate, not by your paging on-call.
Loops that look like loops
Section titled “Loops that look like loops”for "tenant" "var.tenants" { deployment "booking" { name = "booking-${tenant.name}"
container "booking" { image = image("booking") env { TENANT_ID = tenant.name } }
ingress { host = tenant.domain tls = true } }}No range blocks, no nindent, no escaping. Give it a JSON values file and you get one Deployment + Service + Ingress per tenant.
Environment overrides without duplication
Section titled “Environment overrides without duplication”use_vars { environment = "production" image_tag = "v2.4.1"}
override "app" "api" { scale { replicas = 3 }}One file per environment. No overlays/ trees.
Sealed secrets that aren’t magic
Section titled “Sealed secrets that aren’t magic”sealedsecret "db-credentials" { data = { DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO..." }}kdef seal --secret db-credentials --key DATABASE_URL --value "$PLAINTEXT" outputs the encrypted blob. Paste it in. Commit it. Safe.
Import what you already have
Section titled “Import what you already have”kdef import --namespace my-app --output-dir k8s/Turns every Deployment, Service, Ingress, ConfigMap, Secret, CronJob in the namespace into idiomatic .kdef files. Migrate incrementally.
What kdef isn’t
Section titled “What kdef isn’t”- It isn’t a general-purpose config language. If you need CUE’s constraint system or KCL’s type system, use those.
- It isn’t a drop-in Helm replacement for every chart on Artifact Hub — if you pull charts from vendors, you’ll keep using Helm for those.
- It isn’t trying to be fancy. HCL, Go, plain YAML output. That’s the stack.
Ready?
Section titled “Ready?”- Install — binary, Go install, or Linux packages
- Quickstart — import a live namespace, render, diff, apply
- Comparison — kdef vs Kustomize, Helm, CUE, KCL, Pkl