Helm charts in the GitOps flow: HelmRepository as a source, HelmRelease for releases, values management strategies, the install upgrade rollback lifecycle, and dependencies between releases.

In episode 7 you used Kustomize to merge and transform manifests. Now it's Helm's turn — another way, and often a more practical one, to package applications. Flux runs Helm entirely from inside the cluster through the Helm Controller, so you don't need to install Helm on your laptop.
We'll learn about HelmRepository as the chart source, HelmRelease as the release container, values management, the release lifecycle, and dependencies.
Before a chart can be installed, Flux needs to know where the chart comes from. The object is called HelmRepository. Public repo examples are Bitnami and ingress-nginx:
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: bitnami
namespace: flux-system
spec:
interval: 1h
url: https://charts.bitnami.com/bitnamiCreate it via the CLI:
flux create source helm bitnami \
--url=https://charts.bitnami.com/bitnami \
--interval=1hPrivate repos (ChartMuseum, Artifactory, Harbor) require authentication:
flux create secret helm auth-helm \
--username=arman \
--password=$HELM_PASSWORD
flux create source helm internal \
--url=https://helm.internal.acme.com \
--secret-ref=auth-helmBeyond HTTP, HelmRepository also supports OCI — charts packaged as images in a registry like Harbor, ECR, or GHCR. Just set type: oci and the registry URL:
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: ghcr
namespace: flux-system
spec:
type: oci
interval: 10m
url: oci://ghcr.io/armanTip
OCI is becoming the standard for modern chart distribution because it can reuse the same registry as application images, including the existing auth mechanisms and vulnerability scanning.
HelmRelease is a Helm Controller CRD. It references a chart from a HelmRepository and determines the release configuration:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: redis
namespace: databases
spec:
interval: 5m
chart:
spec:
chart: redis
version: "20.x"
sourceRef:
kind: HelmRepository
name: bitnami
values:
architecture: standalone
auth.enabled: trueVia the CLI:
flux create helmrelease redis \
--source=HelmRepository/bitnami \
--chart=redis \
--chart-version="20.x" \
--target-namespace=databasesThings to note:
chart.spec.chart — the chart name in the repository.chart.spec.version — a semver constraint. "20.x" means all version 20 releases.spec.interval — how often Flux checks for a new chart version.spec.values — values that override the chart defaults.Charts are always installed into the same namespace as the HelmRelease, unless you set spec.targetNamespace. To release a chart into another namespace, include a service account so the Helm Controller has permission.
There are two main ways to provide values: inline in spec.values, or pulled from a ConfigMap or Secret via spec.valuesFrom.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: redis
namespace: databases
spec:
chart:
spec:
chart: redis
version: "20.x"
sourceRef:
kind: HelmRepository
name: bitnami
values:
architecture: standalone
valuesFrom:
- kind: ConfigMap
name: redis-values
valuesKey: common.yaml
- kind: Secret
name: redis-secret-values
valuesKey: secrets.yamlOverride order (the last one wins):
values.yaml).spec.values on the HelmRelease.valuesFrom in order of appearance in the list.Warning
valuesFrom and spec.values are merged in a deterministic order, but the result is not a full deep merge. Keep each value in only one place to avoid surprises when the chart is upgraded.
The Helm Controller manages install, upgrade, and rollback automatically. Every time the chart or source changes, Flux performs an upgrade:
flux get helmreleases
flux reconcile helmrelease redis
kubectl describe helmrelease redis -n databasesHelm charts with pre-upgrade, post-install, or test jobs keep working because Flux fully supports Helm hooks. Enable tests so Flux executes test jobs during upgrades:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: redis
namespace: databases
spec:
interval: 5m
test:
enable: true
chart:
spec:
chart: redis
version: "20.x"
sourceRef:
kind: HelmRepository
name: bitnamiinstall.createNamespace: true — Helm creates the namespace automatically on install.upgrade.force: true — force upgrade, recreating immutable resources.upgrade.atomic: true — if the upgrade fails, the entire release is rolled back automatically.uninstall.disableHooks and uninstall.keepHistory — control the behavior when a HelmRelease is deleted.apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: redis
namespace: databases
spec:
install:
createNamespace: true
remediation:
retries: 3
upgrade:
atomic: true
remediation:
retries: 5
rollback:
enable: true
interval: 5m
chart:
spec:
chart: redis
version: "20.x"
sourceRef:
kind: HelmRepository
name: bitnamiImportant
The combination of upgrade.atomic: true and remediation.retries is the best choice for production: a failure never leaves the cluster in a half-broken state.
There are two layers of dependencies to understand: between charts, and between Kustomizations or HelmReleases.
dependsOn between Kustomizations or between HelmReleases:apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
dependsOn:
- name: databases
path: ./apps
prune: true
sourceRef:
kind: GitRepository
name: fleetWith this, the apps Kustomization only runs after databases finishes and all its releases are reported healthy. Orchestration details like health checks and circular dependency prevention are covered fully in episode 10.
Note
A mental model: the Kustomization or HelmRelease determines what gets deployed, while dependsOn determines the order of deployment. Both work together to maintain consistency.
Helm is now part of your GitOps flow:
dependsOn.Next we'll dive into source management: advanced Git references, HelmRepository and OCIRepository, bucket storage, and GPG and Cosign signature verification. See you!