Git, CI/CD & GitOpsLXXVIII · FluxHelm
HelmRelease controller — reconciling Helm charts with versioned values
What you'll learn
- Describe what helm-controller does and which CRD it owns
- Read a HelmRelease spec and identify the chart source, values, and lifecycle fields
- Configure valuesFrom to source values from ConfigMaps and Secrets
- Use `flux create helmrelease` to manage a HelmRelease against a HelmRepository or OCIRepository source
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
helm-controller reconciles Helm charts inside Flux the same way
kustomize-controller reconciles Kustomize directories. It owns
the HelmRelease CRD, consumes a chart source, renders the
chart with declared values, and drives the Helm lifecycle.
What helm-controller does
The controller watches HelmRelease CRDs. For each one it
performs four phases per reconcile:
- Resolve chart. Look up the source CRD in
spec.chart.spec.sourceRef, fetch the artifact, locate the chart. - Resolve values. Combine inline
spec.values, files inspec.valuesFrom, and chart defaults. - Reconcile release. Install if missing; upgrade if present; optionally rollback on upgrade failure.
- Report status. Write the release revision and status
into the
HelmRelease’s status section.
flowchart LR
HR["HelmRelease CRD"] --> HC["helm-controller"]
HC --> C["Resolve chart source"]
C --> V["Resolve values"]
V --> R["Render with helm template"]
R --> D["Diff against live release"]
D -->|"release missing"| IN["helm install"]
D -->|"diff present"| UP["helm upgrade"]
D -->|"diff empty"| OK["Ready=True"]
UP --> K8s["Cluster"]
IN --> K8s
The release status comes from Helm itself, not from
kstatus-based health checks. A HelmRelease that is Ready=True
means the Helm release is deployed.
The HelmRelease spec
A HelmRelease against a HelmRepository:
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: ingress-nginx
namespace: ingress
spec:
releaseName: ingress-nginx
chart:
spec:
chart: ingress-nginx
version: "4.10.x"
sourceRef:
kind: HelmRepository
name: ingress-nginx
namespace: flux-system
interval: 10m0s
values:
controller:
replicaCount: 3
valuesFrom:
- kind: ConfigMap
name: ingress-nginx-config
valuesKey: values.yaml
targetPath: values.yaml
upgrade:
cleanupOnFail: true
driftDetection:
mode: enabled
spec.chart.spec.chartandspec.chart.spec.version- chart name and semver range.
spec.chart.spec.sourceRef- the source CRD (HelmRepository,OCIRepository, orGitRepository).spec.interval- reconcile cadence. Default is10m0s.spec.values- inline values merged with chart defaults.spec.valuesFrom- values sourced from Kubernetes resources (ConfigMaporSecret).spec.upgrade.remediation.retries- retry budget for upgrade failures.spec.driftDetection.mode-enabledre-renders the chart on every reconcile and compares against the live state.
Creating a HelmRelease
flux create helmrelease ingress-nginx \
--source=HelmRepository/ingress-nginx \
--chart=ingress-nginx \
--chart-version="4.10.x" \
--values=controller.replicaCount=3 \
--values-from=ConfigMap/ingress-nginx-config/values.yaml \
--export
--source takes Kind/name. --values repeats per override;
--values-from per referenced ConfigMap or Secret. --export
prints the YAML.
flux get helmreleases
flux get helmrelease ingress-nginx --status
flux reconcile helmrelease ingress-nginx
flux get helmrelease --status includes the release revision,
chart revision, and last-applied values.
Rollback and the install/upgrade lifecycle
helm-controller tracks every Helm revision it installs or upgrades. When a release’s health degrades, the controller does not automatically rollback - that is a deliberate decision the team owns.
flux helm rollback ingress-nginx 3
flux helm rollback $RELEASE $REVISION triggers a Helm
rollback to the named revision. The next reconcile reports
Ready=True against the rolled-back state.
Under the hood
helm-controller runs the actual Helm binary inside its pods. It
shells out to helm install, helm upgrade, helm rollback,
and helm test. This is what guarantees parity with the Helm
the rest of the ecosystem uses.
Production discipline
spec.chart.spec.versionis pinned or semver-bounded. Pin to a patch range like"4.10.x".spec.valuesFromis the source for every secret. No secret inspec.values.spec.driftDetection.mode=enabledis paired with a policy that disallows out-of-bandhelm upgradein the namespace.
Cross-course references
- Kubernetes for Production Sysadmins - Part XXXI (Ingress and Service Mesh) covers ingress-nginx.
Quiz
Knowledge check · 4 questions
Q1. Which spec field in a HelmRelease references external values stored in a Kubernetes ConfigMap or Secret?
Q2. Setting `spec.driftDetection.mode=enabled` causes helm-controller to automatically revert any out-of-band `helm upgrade` applied to the same release.
Q3. Name the three CRD kinds that can be used as `spec.chart.spec.sourceRef`, and identify which one resolves a chart stored in a classic Helm chart registry.
Q4. Diagnose why the HelmRelease for ingress-nginx is reporting Ready=False, reason=InstallFailed, and recommend the fix.
A team runs `flux get helmreleases` and sees ingress-nginx reporting Ready=False, reason=InstallFailed. The team runs `flux logs` against helm-controller and sees: `Error: failed to render template: template: ingress-nginx/templates/controller-deployment.yaml:42: function 'lookup' not defined`. The chart's `values.yaml` calls the `lookup` template function, which requires Helm v3.7+. The cluster's helm-controller runs Helm v3.6.
Passing score: 75%. Answers are checked in this browser.