# A Helm release: chart, values, revisions and the objects Helm owns

> What helm upgrade --install does in the cluster: the chart pulled from an OCI registry, values merged over its defaults, a hook Job, the objects carrying Helm's ownership labels, and one release Secret per revision.

Interactive graph: https://k8wiz.com/graphs/helm-release-chart-values-revisions

Helm is a client. It renders a chart's templates with your values, applies the result, and writes down what it did. That record, one Secret per revision in the release's namespace, is all the state Helm has.

This graph shows what one `helm upgrade --install web` leaves in the `shop` namespace, and what a later upgrade or rollback reads.

## Resources

### Helm chart oci://ghcr.io/acme/charts/web:1.5.0

outside the cluster

Templates, default `values.yaml` and `Chart.yaml`, stored in a registry as an OCI artifact. `--version` picks the tag.

### Values file values-prod.yaml

you write it

Your overrides. Helm merges them over the chart's defaults, then applies `--set` on top. Only what you supply is stored with the release as its user values.

### helm upgrade --install web

made by a build or publishing tool

Renders the templates, compares the result with the last revision's manifest and patches the cluster. With `--wait` it then watches the workloads until they are ready or `--timeout` passes.

### Secret sh.helm.release.v1.web.v2

made by a build or publishing tool, v1, namespace shop

The previous revision, now `superseded`. It holds that revision's chart, values and rendered manifest, which is what `helm rollback web 2` applies again.

### Secret sh.helm.release.v1.web.v3

made by a build or publishing tool, v1, namespace shop

The new revision, of type `helm.sh/release.v1`. Helm writes it as `pending-upgrade` before it touches anything and sets it to `deployed` at the end. While it says pending, other upgrades refuse to start.

### Job web-migrate

made by a build or publishing tool, batch/v1, namespace shop

A `pre-upgrade` hook from the chart. Helm creates it and waits for it to finish before it applies the rest. Hooks are not part of the release manifest, so an uninstall doesn't remove them unless a delete policy says so.

### ConfigMap web

made by a build or publishing tool, v1, namespace shop

Rendered from a template. A checksum of it sits in an annotation on the Pod template, so changing a value here rolls the Pods.

### Deployment web

made by a build or publishing tool, apps/v1, namespace shop

Carries `app.kubernetes.io/managed-by: Helm` and the annotations `meta.helm.sh/release-name` and `meta.helm.sh/release-namespace`. Helm refuses to touch an object of this name that lacks them.

### Service web

made by a build or publishing tool, v1, namespace shop

Part of the same manifest, with the same ownership metadata. If a later chart version drops it, the next upgrade deletes it.

## How the resources connect

- helm upgrade --install web → Helm chart oci://ghcr.io/acme/charts/web:1.5.0: pulls (traffic)
- helm upgrade --install web → Values file values-prod.yaml: -f (references)
- helm upgrade --install web → Secret sh.helm.release.v1.web.v2: reads last revision (references)
- helm upgrade --install web → Secret sh.helm.release.v1.web.v3: writes (creates)
- helm upgrade --install web → Job web-migrate: runs pre-upgrade (creates)
- helm upgrade --install web → ConfigMap web: applies (creates)
- helm upgrade --install web → Deployment web: applies (creates)
- helm upgrade --install web → Service web: applies (creates)
- Deployment web → ConfigMap web: mounts (references)
- Service web → Deployment web: selector (selects)

## How it works

1. **Helm renders the chart.** Helm pulls the chart, merges the defaults, `values-prod.yaml` and any `--set` flags, and runs the templates. A template error stops here, before anything in the cluster changes.
2. **It reads the last revision.** Helm loads the newest release Secret. If that revision is pending, it stops with `another operation is in progress`. If not, its stored manifest becomes the base of a three-way merge with the live objects.
3. **It records the attempt first.** Revision 3 is written as `pending-upgrade`. A Helm process killed after this point leaves the status behind, and the release stays locked until a rollback or a new record clears it.
4. **Hooks run before the manifest.** The `pre-upgrade` Job runs the database migration. Helm waits for it to complete, and a failed hook fails the upgrade.
5. **The objects are patched.** Helm creates what is new, patches what changed and deletes what the chart no longer renders. Each object carries the ownership label and annotations.
6. **The result is written down.** When the workloads are ready, revision 3 becomes `deployed` and revision 2 `superseded`. On failure with `--rollback-on-failure`, Helm applies revision 2's manifest again as revision 4.

## Common mistakes

- A CI job timeout shorter than Helm's `--timeout` kills Helm with the revision still `pending-upgrade`. Every later upgrade fails until `helm rollback` clears it.
- Without `--wait`, Helm 4 waits for hooks and not for your Pods. The command can report success while the rollout is failing.
- An upgrade with no `-f` or `--set` reuses the stored values. One with any of them starts again from the chart's defaults unless you pass `--reuse-values`.
- Helm won't adopt an object it didn't create. `exists and cannot be imported into the current release` goes away once the object has the `managed-by` label and both `meta.helm.sh` annotations.
- `helm history web` keeps ten revisions by default. A rollback target older than that is gone.

## The manifests you write

```yaml
replicaCount: 3

image:
  tag: "2.1"

greeting: Hello from production
```
