Skip to content

Helm Package Manager ​

Last Updated: September 12, 2026 Local validation: Helm 3.21.3 / Helm 4.3.0

Helm renders charts and manages Kubernetes resources and release history. Chart version, appVersion, image tag/digest and release revision are different values. Helm 4 accepts existing apiVersion:v2 charts, but CLI/apply/wait behavior must be checked for the exact version.

Core Concepts and Permissions ​

Helm 3 removed Tiller; clients use their own Kubernetes credentials/RBAC. Chart-repository/OCI-registry communication is separate from Kubernetes API access. Removing Tiller does not make unsafe charts or broad permissions harmless.

Release storage defaults to Secrets in the release namespace; alternatives such as ConfigMap/SQL backends can be configured. Stored release data includes manifests/values and may expose sensitive information. Base64 is not encryption; restrict access to release Secrets.

Complete Local Chart Example ​

examples/platform/helm/reviewed-app contains the eight files below. Helm 3/4 lint/render outputs, packaging, value overrides and rejection of invalid replicaCount were tested. No Kubernetes installation or container execution occurred. Verify image digests, namespaces, hardware and policies before operations.

Chart.yaml ​

yaml
apiVersion: v2
name: reviewed-app
description: Offline Helm teaching chart
type: application
version: 0.1.0
appVersion: "1.30.4"

values.yaml ​

yaml
replicaCount: 1
image:
  repository: nginxinc/nginx-unprivileged
  tag: "1.30.4-alpine"
service:
  port: 8080
resources:
  requests:
    cpu: 100m
    memory: 64Mi
  limits:
    cpu: 500m
    memory: 128Mi
env:
  LOG_LEVEL: info

values.schema.json ​

json
{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [
    "replicaCount",
    "image",
    "service"
  ],
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 0,
      "maximum": 5
    },
    "image": {
      "type": "object",
      "required": [
        "repository",
        "tag"
      ],
      "properties": {
        "repository": {
          "type": "string",
          "minLength": 1
        },
        "tag": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "service": {
      "type": "object",
      "required": [
        "port"
      ],
      "properties": {
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        }
      }
    },
    "env": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  }
}

templates/_helpers.tpl ​

text
{{- define "reviewed-app.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "reviewed-app.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- end -}}

templates/deployment.yaml ​

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "reviewed-app.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "reviewed-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "reviewed-app.selectorLabels" . | nindent 8 }}
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 101
        runAsGroup: 101
        fsGroup: 101
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: web
        image: {{ printf "%s:%s" .Values.image.repository .Values.image.tag | quote }}
        ports:
        - name: http
          containerPort: 8080
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop: [ALL]
        resources:
          {{- toYaml .Values.resources | nindent 10 }}
        env:
          {{- range $key, $value := .Values.env }}
        - name: {{ $key | quote }}
          value: {{ $value | quote }}
          {{- end }}
        readinessProbe:
          httpGet:
            path: /
            port: http
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir:
          sizeLimit: 64Mi

templates/service.yaml ​

yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "reviewed-app.fullname" . }}
spec:
  type: ClusterIP
  selector:
    {{- include "reviewed-app.selectorLabels" . | nindent 4 }}
  ports:
  - name: http
    port: {{ .Values.service.port }}
    targetPort: http

templates/NOTES.txt ​

text
Inspect the rendered resources and prepare namespace/image compatibility before installation.
Release: {{ .Release.Name }}
Namespace: {{ .Release.Namespace }}

.helmignore ​

text
*.private

All helpers are defined, the Service targets a named container port, securityContext is in the manifest, and resources/env are wired from values into templates. An unused values entry has no effect. This basic chart does not create a database, Ingress or autoscaler.

Local Checks ​

Run from the repository root and check the selected binary with helm version --short.

bash
helm lint examples/platform/helm/reviewed-app
helm template demo examples/platform/helm/reviewed-app --namespace example
helm template demo examples/platform/helm/reviewed-app   --set replicaCount=3 --set-string env.MAX_CONNECTIONS=100
helm package examples/platform/helm/reviewed-app --destination ./chart-packages

Lint/template success does not validate admission, CEL, RBAC, image execution, Service connectivity or readiness. Test hooks must actually execute in a cluster. helm template --api-versions supplies offline capabilities; it does not install CRDs.

Commands and Helm 3/4 Differences ​

PurposeExample and limits
Repositorieshelm repo add/update/list/remove, helm search repo; OCI registries have separate login/pull flows
Installhelm install demo ./chart -n example --create-namespace; verify namespace/release existence
Install or upgradehelm upgrade --install; hooks, random values and external state need not be idempotent
Inspecthelm list -n example, status/history/get values/get manifest; protect sensitive output
Computed valueshelm get values demo -n example --all includes chart defaults
Rollbackhelm rollback demo REVISION -n example; revision is not an image tag
Uninstallhelm uninstall demo -n example; inspect PVC/CRD/hook/external-resource lifecycle

The old stable repository is archival, not a current default. Verify external chart/image availability, licensing, support and security and pin chart versions. Old Bitnami PostgreSQL12/Redis17 dependencies are no longer this example's defaults.

Dry Runs and Waiting ​

Helm 4.3 distinguishes --dry-run=client and --dry-run=server. In this environment, 4.3 client mode passed without a cluster; 3.21.3 install client dry-run attempted cluster access and failed. Use the verified helm template path for offline rendering. Server mode needs cluster access/permissions and does not prove all webhook/external side effects.

In Helm 4.3, omitted --wait defaults to hookOnly; specifying --wait defaults to watcher, with legacy also available. --rollback-on-failure rolls failed upgrades back to a prior successful release. Its name differs from Helm 3's --atomic. --force-replace and --force-conflicts separately control replacement and server-side-apply conflicts; check exact-version help.

Rollback is not a transaction reversing DB migrations, external API effects or deleted data. Distinguish timeout, Pod readiness, Job completion and application SLOs.

Templates and Values ​

Chart, Release, Values and Capabilities are context objects. range/with change dot context; use $ when the root is needed. Capabilities reflects supplied discovery information, not universal compatibility.

include returns named-template output as a string and can be piped into nindent, which also inserts a newline. Prefix helper names to avoid subchart collisions and avoid unnecessary selector changes across upgrades.

default/coalesce treat false, zero, empty strings and collections as empty. Check existence/types separately when preserving explicit false/zero. A default does not protect every nested lookup whose parent map is absent.

values.yaml is data: embedded {{ .Values... }} is not automatically evaluated again. Chart authors can explicitly use tpl where needed, but must review input trust and template privileges. The former subchart storageClass and Blue/Green selector strings were not automatically wired.

For repeated files/overrides, rightmost values win; understand map merging and list replacement. Save dev/staging/prod as separate files instead of duplicate keys in one YAML document. Use --set-string for numeric-looking strings and version-supported --set-json for structures.

--reuse-values, --reset-values and --reset-then-reuse-values combine prior release values and new defaults differently. Review computed values and rendered diffs instead of relying on implicit behavior.

Dependency Management ​

Chart.yaml declares dependency names, versions, repositories and optional aliases/conditions. This fragment assumes a prepared local helper subchart.

yaml
dependencies:
- name: helper
  alias: cache
  version: 0.1.0
  repository: file://../dependency-child
  condition: cache.enabled

With an alias, place values under cache and use a matching condition. Test behavior when a condition path is absent. Global values matter only when the subchart consumes them; import-values requires matching child/parent export structure.

dependency update resolves Chart.yaml constraints and writes Chart.lock. build uses locked versions; without a lock it can resolve similarly to update. A lock alone does not establish tamper resistance, pinned runtime images or complete reproducibility. Manage chart digests/signatures, supply paths and image revisions. Local file-dependency update/build and alias on/off were exercised with Helm 3/4.

Hooks, CRDs and Tests ​

Pre/post install, upgrade, rollback, delete and test hooks execute at their lifecycle stages. Lower weights run first; inspect kind/name ordering for ties. A pre-install migration may run before the chart's ordinary database resource exists.

Hook Jobs/Pods need real executables, images, Services/Secrets, permissions, timeouts and repeat-safe behavior. Plan cleanup with before-hook-creation/hook-succeeded/hook-failed and Job TTLs; uninstall need not remove all hook resources. Interpret post-install readiness in conjunction with --wait.

CRDs under crds/ differ from normal templates. Do not assume automatic upgrade/deletion or rollback of CRD schemas. Use explicit migration and custom-resource retention plans; deleting a CRD can remove custom-resource data.

helm test runs declared hooks. Simple HTTP connectivity does not validate databases, security, load or recovery. Blue/Green/canary needs real Deployments, Services/mesh routes, controllers and metric/rollback conditions. Values alone do not implement progressive delivery.

GitOps and Security ​

Argo CD generally uses Helm as a template renderer, distinct from owning Helm release lifecycle. Flux helm-controller reconciles HelmRelease. Verify source/chart revisions, valuesFrom namespace/precedence, hook mapping, pruning and ownership; avoid competing controllers.

Do not place secrets in chart defaults, --set arguments or debug output. --hide-secret covers Kubernetes Secret output during dry-run, not general redaction of all values/logs. Existing Secret references delivered through app environment variables still violate file-credential policies. Use approved Secret volumes and file reread/rotation paths.

Prepare current APIs such as ESO v1 and their controllers separately. Sealed Secrets/helm-secrets require their controller/plugin, key/KMS access and decryption workflow; they are not Helm core features. Inspect whether decrypted values enter release records or logs.

ServiceAccounts/Roles alone do not grant workload permissions. Connect RoleBindings and serviceAccountName when required, and do not grant all-Secret get/list/watch merely for a Secret volume. The demo web chart needs no Kubernetes API credentials and disables token automount.

Troubleshooting Order ​

SymptomInvestigation and correction
Reused release nameCheck namespace/state/history; choose intended upgrade or a new name
Existing-resource collisionInspect owner annotations/labels/controllers; use a reviewed adoption/migration or rename
Failed releaseCheck causes/events/history and retry with verified revision/configuration
Missing helperCheck definitions, names, scope and root context
Schema failureInspect final merged values, types, required fields and ranges

Deletion/force flags are not universal fixes. Review diffs, immutable fields, data retention and other controllers before choosing a mutation.

Verification and References ​

All 764 guide lines, 462 quiz lines per locale and 58 unique blocks were reviewed. Checks covered complete-chart Helm 3/4 lint/template/package, overrides/negative schemas and local dependencies/aliases; 4.3 client dry-run passed. The 3.21.3 install dry-run cluster-access failure is recorded. No real Kubernetes installation, upgrade, rollback, hooks or app HTTP behavior was validated.

Helm quiz