How does applying helm template output with kubectl differ from running helm install?
answer
- One command talks to a cluster, one does not
- Ask what is left behind in the namespace
- Which commands need a release to read
- No release record means no list, history or rollback
- crds/ needs --include-crds when rendering
basics
~20 shelm template only renders a chart to YAML on stdout; helm install renders it, applies it, and records the release in the namespace. Objects applied from rendered YAML have no release record, so helm list, helm history and helm rollback see nothing.
solid answer
~50 s`helm template` is a pure renderer: it merges values, evaluates everything under `templates/` and prints manifests. Nothing is sent to a cluster, so whatever applies that YAML - a pipeline, a controller, a person - is what creates the objects. `helm install` (or `helm upgrade --install`) does the same render, applies the result, and writes a release record into the namespace holding the rendered manifest, the supplied values and a revision number. That record is what `helm list`, `helm status`, `helm get manifest`, `helm history` and `helm rollback` read, so a rendered-and-applied deployment is invisible to all of them. Three further gaps bite in practice: hook annotations stop ordering anything because no installer is sequencing them, files under `crds/` are left out of the render unless you pass `--include-crds`, and nothing deletes objects the chart stopped rendering, because there is no stored previous manifest to diff against.
code
bash · 10 lines# Helm renders, applies, and records a release
helm upgrade --install ingest-gw ./telemetry-gateway -n telemetry --wait=watcher
helm list -n telemetry
helm history ingest-gw -n telemetry
# Rendered ahead of time, applied by something else
helm template ingest-gw ./telemetry-gateway -n telemetry --include-crds > out.yaml
kubectl apply -n telemetry -f out.yaml
helm list -n telemetry # no rows
helm rollback ingest-gw -n telemetry # nothing to roll back togo deeper
Be ready to say plainly that helm template prints YAML and never touches a cluster, while helm install renders and applies it. Knowing that only the second one leaves a release behind is most of the answer.
Explain what the release record holds - rendered manifest, supplied values, revision number - and name the commands that go looking for it. Mentioning that crds/ needs --include-crds when rendering marks you out.
Show you have operated the rendered model: orphaned objects nobody deletes, hook annotations that order nothing, and how you reproduce a render with the same release name and namespace to diff against what is live.
Own the tradeoff before the team commits: which Helm features your estate actually depends on, and what you put in place of history, rollback and hook ordering once a controller renders charts itself.
## The install path has two halves Every Helm install is two distinct pieces of work bolted together. **The render half.** Helm loads the chart, coalesces values (chart defaults, then subchart values, then whatever the caller supplied), validates them against `values.schema.json` if the chart ships one, and evaluates every file under `templates/` as a Go text template with the built-in objects `.Values`, `.Release`, `.Chart`, `.Capabilities` and `.Files` in scope. The product is a stream of YAML documents. **The delivery half.** Helm sends those documents to the cluster, runs hook manifests at their events in weight order, optionally waits for the workloads, and writes a release record - a Secret named `sh.helm.release.v1.<name>.v<rev>` in the release namespace - carrying the rendered manifest, the values the caller supplied, and the revision number. `helm template` runs the first half and stops, printing to stdout. `helm install` and `helm upgrade --install` run both. That single sentence explains every behaviour difference below. ## What a rendered pipeline gains The manifests that will exist are ordinary text: reviewable in a diff before merge, greppable, and identical whichever machine renders them. One applier touches the cluster, so there is exactly one credential path and one audit trail. And no Helm state lives in the cluster to get stuck, grow, or need pruning. ## What it gives up, item by item **1. The release record, and everything that reads it.** `helm list` enumerates release records in a namespace; with none written it prints nothing, no matter how many chart-derived objects are running. `helm history`, `helm get manifest`, `helm get values`, `helm status`, `helm rollback` and `helm uninstall` all key off the same record. The objects are perfectly real - Helm simply has no idea they came from a chart. Note this is a Helm 4 statement as much as a Helm 3 one: `helm list -a` was removed in Helm 4 and would not have conjured a missing record anyway. **2. Deletion of resources dropped from the chart.** `helm upgrade` works out what to remove by diffing the new manifest against the manifest stored with the previous revision. With no stored manifest, Helm contributes nothing to removal: an object whose template you deleted keeps running unless the thing applying the YAML separately tracks what it applied last time and removes the difference. **3. Hooks.** `helm template` still prints hook-annotated manifests unless `--no-hooks` is passed, but `helm.sh/hook`, `helm.sh/hook-weight` and `helm.sh/hook-delete-policy` are instructions to an installer. Applied as ordinary YAML they are inert annotations, so a pre-upgrade migration Job lands in the same batch as the new pods instead of ahead of them, and `helm test` has no release to run against. **4. CRDs.** Files under `crds/` are never templated. `helm install` applies them first and skips ones already present; `helm template` omits them entirely unless you pass `--include-crds`. A rendered pipeline that forgets that flag ships custom resources whose definitions were never created. **5. Waiting and failure handling.** `--wait`, `--wait-for-jobs`, `--timeout` and `--rollback-on-failure` are properties of the delivery half. Rendering produces no rollout to watch and no failure to react to. **6. Cluster awareness at render time.** Rendering offline uses a built-in capability set, so `.Capabilities.KubeVersion` and `.Capabilities.APIVersions` describe defaults rather than the target cluster unless `--kube-version` and `--api-versions` are supplied. `.Release.IsInstall` is true and `.Release.IsUpgrade` false unless `--is-upgrade` is passed, and `.Release.Revision` never advances - a chart that names an object after the revision produces the same name forever. **7. Ownership metadata.** When Helm applies resources it stamps them with the label `app.kubernetes.io/managed-by: Helm` and the annotations `meta.helm.sh/release-name` and `meta.helm.sh/release-namespace`. Rendered YAML carries only whatever labels the chart itself writes, which is why handing those objects back to Helm later is its own small project. ## Reproducing a render The render is deterministic given chart version, values, release name, namespace and any capability flags. That is the operational lever in a rendered pipeline: re-run `helm template` with exactly those inputs and diff the output against what is live. Get the release name wrong and every object whose name derives from `.Release.Name` differs, so the diff is useless - which is the first thing to check when a reproduction looks like a total rewrite. ## Choosing between them Render-and-apply is a reasonable model when the applier supplies its own ordering, pruning and health assessment, and when the charts you deploy do not lean on hooks or `crds/`. Letting Helm install is the right default when you want history, rollback and hook ordering to be someone else's already-solved problem.
- Does helm template include the files in a chart's crds/ directory?Not by default. `helm template` omits `crds/` unless you pass `--include-crds`, while `helm install` applies those files first, ahead of the templated manifests, and skips any definition already present. A render-and-apply pipeline that never passes the flag ships custom resources with no definitions behind them, and the failure surfaces on first apply as an unknown kind rather than as anything Helm reports.
- You delete a template from the chart and re-run the pipeline. What happens to the object it used to render?Under `helm upgrade` it is deleted, because Helm diffs the new manifest against the manifest stored with the previous revision. With render-and-apply there is no stored previous manifest, so Helm contributes nothing: the object keeps running unless whatever applies the YAML tracks its own previous output and removes what disappeared. Orphans of this kind are the most common surprise after moving off helm upgrade.
- Can helm template tell you what API versions the target cluster actually supports?Not on its own - an offline render evaluates `.Capabilities.APIVersions` and `.Capabilities.KubeVersion` against a built-in default set. You either pass `--kube-version` and `--api-versions` to match the target, or render against the live cluster with a server-side dry run. Until you do, a chart that branches on capabilities can render one way in the pipeline and a different way on the cluster.
helm template is printing the assembly instructions; helm install is building the furniture and keeping the receipt. Without the receipt nobody can tell what was bought, what changed, or how to return it.
saying these in an interview costs you the question
- Says helm template contacts the cluster to validate manifests
- Expects helm list to show a rendered-and-applied deployment
- Thinks helm rollback works without any release record
- Assumes crds/ appears in helm template output by default
- Believes deleting a template automatically deletes the live object
- Calls helm template a dry run of helm install