What must a Helm deploy identity be able to write beyond the objects its chart renders?
answer
- The chart is not the whole write set
- Helm keeps its own state in the namespace
- One object per revision, plus pruning
- sh.helm.release.v1.<name>.v<rev>
basics
~10 sThe release records. With the default storage driver Helm writes one Secret per revision, sh.helm.release.v1.<name>.v<rev>, in the release namespace, and must also read, list and delete them for upgrades, history and --history-max pruning.
solid answer
~40 sBeyond every kind the chart renders, a Helm deploy identity needs full read and write access to the release storage in the release namespace: with the default driver that is Secrets named `sh.helm.release.v1.<name>.v<rev>`. Helm lists them to find the current revision, reads the previous one to compute the upgrade, creates a new one per revision, updates statuses, and deletes the oldest once history exceeds `--history-max` (default 10). It must also read the live objects it is about to change. Two extras appear at the edges: `--create-namespace` is a cluster-scoped write, and anything in `crds/` is installed on first install, which is also cluster-scoped. Setting `HELM_DRIVER` to `configmap` moves the storage to ConfigMaps; `sql` moves it out of the cluster entirely.
code
bash · 5 lines# What the chart contributes to the write set
helm template tiles ./tile-server -n geo-staging | grep '^kind:' | sort -u
# What Helm itself writes in the release namespace
kubectl get secrets -n geo-staging --field-selector type=helm.sh/release.v1go deeper
Know that Helm remembers releases by writing an object into the release's namespace, not into a file on your laptop, and that helm list is reading those objects back.
Explain the full lifecycle of the release records — list, read, create, update, delete on prune — and name the default Secret naming scheme. Mention --create-namespace and crds/ as the two writes that leave the namespace.
Show you would derive a deploy identity's write set from a render plus Helm's own state, and that you have seen the failure where a pipeline breaks at the history cap rather than on the change that was shipped.
Weigh where release state should live across an estate: in-namespace Secrets keep the boundary simple but expose stored values to namespace readers, while an external driver centralises state and creates a new dependency on the delivery path.
## The chart is not the whole write set People scope a deploy identity by reading the chart: it renders a Deployment, a Service and a ConfigMap, so the identity gets those three kinds and the install is expected to work. It does not, because a Helm release is not only the rendered objects. Helm keeps its own state, in the cluster, in the release's namespace, written with the same credentials. ## Release storage Every revision of a release is persisted as one record. With the default storage driver that record is a **Secret named `sh.helm.release.v1.<name>.v<rev>`** — `sh.helm.release.v1.tiles.v3` for revision 3 of a release called `tiles` — living in the release namespace, holding the chart, the values it was rendered with, the rendered manifest and the release status. Helm touches it constantly: - **list** the records in the namespace, to discover the release and its latest revision — this is also exactly what `helm list` reads; - **read** the newest record on upgrade, because the previous rendered manifest is the input to the diff Helm applies; - **create** a new record for the new revision; - **update** records as status moves through pending, deployed, failed and superseded; - **delete** records when history exceeds `--history-max` (default 10), and delete them all on `helm uninstall` unless `--keep-history` is used. An identity that may write Deployments but not Secrets in that namespace therefore cannot install anything there at all, and the failure looks nothing like a chart problem. The reverse is also worth stating: because the record holds the values, anyone who can read Secrets in the release namespace can read every stored revision's values, whether or not they can install. `HELM_DRIVER` changes where this lands: `configmap` stores the same records as ConfigMaps, `memory` keeps them only for the life of the process, and `sql` puts them in an external database — the last of which removes the in-cluster storage requirement but adds a connection string to manage. ## Reading before writing An upgrade is not a blind write. Helm compares three things — the previously stored manifest, the newly rendered one, and the live object — so the identity must be able to read the objects it manages, not just create them. In Helm 4 the write itself is a server-side apply by default (`--server-side` is a boolean defaulting to true on install, and a string defaulting to `auto` on upgrade and rollback, which inherits whatever the previous revision used). That matters here because the write reaches the API server as a patch rather than as the Helm 3 client-side create-or-update, so an identity scoped tightly around one write verb can behave differently on a Helm 4 release than on one first installed by Helm 3. ## The cluster-scoped edges Two very ordinary flags quietly leave the namespace: - `--create-namespace` creates a Namespace, and Namespaces are cluster-scoped objects. A pipeline that provisions its own environment therefore needs a cluster-scoped write that a per-namespace identity does not have. - Anything under the chart's `crds/` directory is installed on first install if the CRD is not already present. CustomResourceDefinitions are cluster-scoped. They are never templated, never upgraded and never deleted by Helm, which means the requirement is a one-time one and is a good candidate for pre-installing under a different identity. ## Worked example A geospatial tile-server chart is installed by a pipeline into `geo-staging`. The chart renders 14 namespaced objects. The pipeline's identity was scoped to exactly those kinds, and the first `helm upgrade --install` fails before creating any of them, because Helm could not list release records in the namespace. Adding Secret access in `geo-staging` fixes it; nothing about the chart changed. After 11 upgrades the pipeline also starts deleting records, because the default history cap of 10 has been reached — a delete permission that nobody predicted from reading the chart. ``` $ kubectl get secret -n geo-staging | grep sh.helm.release sh.helm.release.v1.tiles.v2 helm.sh/release.v1 1 6d sh.helm.release.v1.tiles.v3 helm.sh/release.v1 1 2d ``` ## How to derive the write set honestly Render the chart offline with `helm template` and list the distinct `kind` values — that gives you the chart's contribution. Then add release storage in the target namespace, plus a namespace create if the pipeline uses `--create-namespace`, plus the `crds/` contents if this is a first install. Anything left over — a hook Job, a test Pod — will show up in the render too, because hooks are ordinary manifests with an annotation.
- What breaks if the identity can create release records but not delete them?Installs and upgrades work until history reaches `--history-max`, which defaults to 10. On the eleventh upgrade Helm tries to prune the oldest revision record and fails, so a pipeline that ran green for weeks starts failing on an operation nobody changed. `helm uninstall` also cannot clean up, since removing a release means deleting its records. Raising or lowering `--history-max` changes when you hit it, not whether.
- Does `HELM_DRIVER=sql` remove the need for in-cluster storage permissions?It moves the release records out of the cluster into an external SQL database, so the identity no longer needs to write Secrets or ConfigMaps for Helm's own state — it still needs everything the chart renders. In exchange you now operate a database that is on the critical path for every install, upgrade, rollback and `helm list`, and its connection string becomes a credential the pipeline holds. Most teams keep the default driver.
- Why does an upgrade need to read the release record rather than just re-render the chart?Helm computes what changed by comparing the manifest stored with the previous revision against the newly rendered one and against the live objects. The stored manifest is what tells Helm which objects the release used to own, and therefore which ones have been removed from the chart and should be deleted. Without it Helm could add and change objects but could never prune.
saying these in an interview costs you the question
- Scopes the identity to the chart's kinds only
- Thinks Helm state lives on the client machine
- Says a controller writes the release records
- Forgets deletes are needed for history pruning
- Believes --create-namespace is a namespaced write
- Assumes crds/ install needs no extra permission