kdef
Declarative HCL for Kubernetes: typed, loopable, renders plain YAML.
Latest release: v0.7.7
One block, every manifest you need
Declare a deployment. Get a Deployment, Service, Ingress, Certificate, and HPA, rendered as plain Kubernetes YAML you can read, diff, and commit.
api.kdef
deployment "api" {
namespace = "production"
container "api" {
image = image("api")
port "8080" "http" {
health = "/health"
}
env {
APP_ENV = var.environment
DATABASE_URL = secret("db-credentials", "url")
}
resources {
cpu = "300m..800m"
memory = "256M..1G"
}
}
service {
port "8080" "http" {}
}
ingress {
host = "api.example.com"
tls = true
}
autoscale {
min = 2
max = 10
cpu = 70
}
}
Rendered Kubernetes YAML
# Generated by kdef 0.7.5
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
name: api
namespace: production
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/name: api
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
name: api
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: api
template:
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
spec:
containers:
- env:
- name: APP_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
key: url
name: db-credentials
image: registry.example.com/api:1.4.2
livenessProbe:
httpGet:
path: /health
port: 8080
name: api
ports:
- containerPort: 8080
name: http
protocol: TCP
resources:
limits:
cpu: 800m
memory: 1G
requests:
cpu: 300m
memory: 256M
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
name: api
namespace: production
spec:
maxReplicas: 10
metrics:
- resource:
name: cpu
target:
averageUtilization: 70
type: Utilization
type: Resource
minReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
name: api
namespace: production
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- backend:
service:
name: api
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- api.example.com
secretName: api-example-com-tls
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
labels:
app.kubernetes.io/managed-by: kdef
app.kubernetes.io/name: api
kdef.dev/version: 0.7.5
name: api-example-com-tls
namespace: production
spec:
dnsNames:
- api.example.com
issuerRef:
kind: ClusterIssuer
name: letsencrypt-production
secretName: api-example-com-tls
Why people pick kdef
Typed variables
Strings, numbers, bools, enums, lists. Typos caught at validate-time, not 3AM.
Native loops
One for block → a Deployment per tenant, env, or region. No Helm template gymnastics.
Transparent output
Plain YAML every render. kdef diff shows exactly what will change.
Import existing
kdef import --namespace my-app turns a live cluster into .kdef files.
Sealed secrets
First-class sealedsecret block. Encrypt with kdef seal, commit safely.
Works with GitOps
Flux controller and ArgoCD plugin ship in the box.
kdef sits between Kustomize and Helm
Kustomize
No variables. No loops. Patch files multiply for every environment.
Helm
Go templates over YAML. Opaque, error-prone, needs helm template to see what's going out.
kdef
HCL syntax, typed variables, native loops, renders plain YAML. One block = app + service + ingress + HPA.
Install
# Go install
go install github.com/gsid-nl/kdef/cmd/kdef@latest
# Or grab a binary from releases
# https://github.com/gsid-nl/kdef/releases
Platform packages (.deb, .rpm, .apk) are on the releases page.
Next steps
- Quickstart: import a live namespace, render, diff, apply, in five minutes.
- API with sidecar: real-world deployment with API, nginx, a sealed DB password and ingress.
- Per-tenant loops: one block, N tenants, driven by a JSON values file.
- GitOps with Flux: point a KdefRelease at a GitRepository. Done.
Start here
- Why kdef: the case for a purpose-built Kubernetes deployment language.
- Install: binaries, Go install, Linux packages, or build from source.
- Quickstart: import a live namespace, render, diff and apply.
Guides
- API with sidecar: API container, nginx sidecar, sealed DB password, ingress with cert-manager.
- Sealed secrets: encrypt with kubeseal, commit safely, reference from deployments.
- Multi-app layout: ship several apps together with
root.kdef. - Environment overrides: one project for staging, production and anything else.
- Per-tenant loops: one block, N tenants, from a JSON values file.
- Import from cluster: turn an existing namespace into
.kdeffiles.
Reference
- Block types: every block kdef understands, from
root.kdefanddeploymenttocrd. - Variables: declarations, types,
env, overrides, ingress defaults, environment overrides. - Functions:
image(),secret(),configmap()andfile(). - Conditionals and loops: ternaries,
ifblocks andforloops. - CLI commands: every command and its flags.
- Comparison: kdef against Kustomize, Helm, CUE, KCL and Pkl.
- Release notes: what changed in each release, on GitHub.
GitOps
- Flux CD: a
KdefReleaseper GitRepository, rendered and applied on every commit. - ArgoCD: kdef as a Config Management Plugin sidecar.
Tooling
- Editors & LSP:
kdef-lspin VS Code, JetBrains and Neovim, plus the pre-commit hook.