Render Manifests Before Apply: A CI Pattern
Rendering templated config to a concrete file and diffing it before apply turns silent templating bugs into a visible, reviewable CI step.
Every templater in this cluster shares one CI best practice: render to an artifact, inspect it, then apply. This page is the pattern the individual command pages point back to.
What it does
The pattern separates rendering from applying. A render step (gomplate, ytt, jsonnet, cue export, jinja2, envsubst, and so on) writes a concrete manifest to a file. A diff step compares it against the live cluster or the previous render. Only then does an apply step ship it.
Common usage
# 1. render to an artifact
ytt -f config/ > rendered.yaml
# 2. diff against the live cluster (non-zero exit on drift)
kubectl diff -f rendered.yaml || true
# 3. apply the exact file you diffed
kubectl apply -f rendered.yamlWhy it matters
| Risk if you skip it | What render-then-apply catches |
|---|---|
| Unset variable renders empty | A blank image: or <no value> is visible in the artifact |
| Wrong overlay merged | The diff shows unexpected fields before they hit the cluster |
| Secret leaked into output | You can scan the rendered file before it is stored or logged |
| Tool version drift | A pinned renderer + committed artifact makes changes reviewable |
In CI
Upload the rendered manifest as a build artifact so a human can review exactly what will be applied, and apply that same file rather than re-rendering at apply time (re-rendering can pick up different env). Keep secrets out of the artifact: render placeholders and inject secrets at deploy time, or scrub the file before upload.
Common errors in CI
"error validating data: ... unknown field" from kubectl apply on a rendered file means the template produced an invalid manifest; the render-then-diff step surfaces it before apply. A rendered file containing a literal <no value>, an empty image: , or a stray $VAR is the fingerprint of a missing template variable. kubectl diff exits 1 on any difference, so wrap it with || true if you only want to log drift.