HelmRelease Resource
Chart Source Options
# Option A: Chart from a HelmRepository
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: nginx-ingress
namespace: flux-system
spec:
interval: 1h
chart:
spec:
chart: ingress-nginx # Chart name in the repo
version: "4.10.*" # Semver constraint
sourceRef:
kind: HelmRepository
name: ingress-nginx
namespace: flux-system
targetNamespace: ingress-nginx
install:
createNamespace: true
values:
controller:
replicaCount: 2
# Option B: Chart from an OCI HelmRepository
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 10m
chart:
spec:
chart: podinfo
version: ">=6.0.0"
sourceRef:
kind: HelmRepository
name: podinfo # OCI-type HelmRepository
namespace: flux-system
targetNamespace: default
# Option C: Chart from a GitRepository (chart stored in Git)
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: flux-system
spec:
interval: 5m
chart:
spec:
chart: ./charts/grade-api # Path to chart within the repo
sourceRef:
kind: GitRepository
name: grade-api # GitRepository defined in Part 1
namespace: flux-system
reconcileStrategy: ChartVersion # Or: Revision (use git SHA)
targetNamespace: grade-api
install:
createNamespace: true
Injecting Values
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: flux-system
spec:
interval: 5m
chart:
spec:
chart: ./charts/grade-api
sourceRef:
kind: GitRepository
name: grade-api
# ── INLINE VALUES ─────────────────────────────────────────────
values:
replicaCount: 2
image:
repository: ghcr.io/your-org/grade-api
tag: latest # Image Updater will modify this (Part 4)
service:
type: ClusterIP
port: 8080
# ── VALUES FROM CONFIGMAPS / SECRETS ──────────────────────────
# valuesFrom merges values in order (later entries win)
valuesFrom:
- kind: ConfigMap
name: grade-api-values # Environment-specific config
valuesKey: values.yaml # Key in the ConfigMap (default: values.yaml)
- kind: Secret
name: grade-api-secrets # Sensitive values (DB password, etc.)
valuesKey: values.yaml
optional: true # Don't fail if Secret doesn't exist
- kind: ConfigMap
name: grade-api-values
valuesKey: db_host # Reference a single key as a dot-path
targetPath: postgresql.host # Map it to a specific values path
# ConfigMap with environment values
apiVersion: v1
kind: ConfigMap
metadata:
name: grade-api-values
namespace: flux-system
data:
values.yaml: |
replicaCount: 3
postgresql:
host: postgres.grade-api.svc.cluster.local
database: grade_api_prod
ingress:
enabled: true
host: api.grade.company.com
db_host: "postgres.grade-api.svc.cluster.local"
Upgrade & Rollback
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: flux-system
spec:
interval: 5m
chart:
spec:
chart: ./charts/grade-api
sourceRef:
kind: GitRepository
name: grade-api
# ── INSTALL BEHAVIOUR ─────────────────────────────────────────
install:
createNamespace: true
remediation:
retries: 3 # Retry install up to 3 times on failure
# ── UPGRADE BEHAVIOUR ─────────────────────────────────────────
upgrade:
remediation:
retries: 3
remediateLastFailure: true # Rollback on last retry failure
cleanupOnFail: true # Delete new resources if upgrade fails
crds: CreateReplace # Upgrade CRDs during helm upgrade
# ── ROLLBACK BEHAVIOUR ────────────────────────────────────────
rollback:
timeout: 5m
cleanupOnFail: true
recreate: false # Re-create pods after rollback
# ── TEST ──────────────────────────────────────────────────────
test:
enable: true # Run helm test after install/upgrade
ignoreFailures: false
# ── DRIFT DETECTION ──────────────────────────────────────────
driftDetection:
mode: enabled # Detect and remediate drift
ignore:
- paths: ["/spec/replicas"] # Allow HPA to manage replicas
target:
kind: Deployment
values:
replicaCount: 2
# Trigger immediate reconciliation
flux reconcile helmrelease grade-api -n flux-system
# Check HelmRelease status
flux get helmreleases -n flux-system
# NAME REVISION SUSPENDED READY MESSAGE
# grade-api 1.2.3 False True Release reconciliation succeeded
# Debug a failed HelmRelease
flux describe helmrelease grade-api
kubectl describe helmrelease grade-api -n flux-system
# Manual rollback (suspend first)
flux suspend helmrelease grade-api
helm rollback grade-api 1 -n grade-api
flux resume helmrelease grade-api
# Force a full reinstall
flux suspend helmrelease grade-api
helm uninstall grade-api -n grade-api
flux resume helmrelease grade-api
OCI Chart Repositories
Pushing Charts to OCI
# Login to GHCR
echo $GITHUB_TOKEN | helm registry login ghcr.io -u $GITHUB_USER --password-stdin
# Package your chart
helm package ./charts/grade-api
# Successfully packaged chart and saved it to: grade-api-1.0.0.tgz
# Push to GHCR OCI registry
helm push grade-api-1.0.0.tgz oci://ghcr.io/your-org/charts
# Verify
helm show chart oci://ghcr.io/your-org/charts/grade-api --version 1.0.0
# In CI (GitHub Actions) — automate chart push on version bump
# .github/workflows/helm-publish.yaml
name: Helm Chart Publish
on:
push:
paths:
- 'charts/**'
tags:
- 'chart-*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Package and push
run: |
helm package charts/grade-api
helm push grade-api-*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts
Consuming OCI Charts
# OCI HelmRepository source
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: grade-api-charts
namespace: flux-system
spec:
type: oci
interval: 5m
url: oci://ghcr.io/your-org/charts
secretRef:
name: ghcr-credentials # docker-registry type secret
---
# HelmRelease consuming OCI chart
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: flux-system
spec:
interval: 5m
chart:
spec:
chart: grade-api
version: ">=1.0.0 <2.0.0" # Semver range — picks latest matching
sourceRef:
kind: HelmRepository
name: grade-api-charts
targetNamespace: grade-api
install:
createNamespace: true
values:
replicaCount: 2
Cross-Namespace Patterns
# By default, sources and HelmReleases must be in the same namespace.
# To reference a source across namespaces, use a HelmChart CRD
# (helm-controller manages this automatically for you):
# Allow cross-namespace source references (set in helmrelease spec):
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: apps # HelmRelease in 'apps' namespace
spec:
interval: 5m
chart:
spec:
chart: grade-api
version: ">=1.0.0"
sourceRef:
kind: HelmRepository
name: grade-api-charts
namespace: flux-system # Source in 'flux-system' namespace
# Cross-namespace references require allowing it in Flux's NetworkPolicy.
# Check if cross-namespace is enabled:
kubectl get configmap -n flux-system flux-system -o yaml | grep allow-cross-namespace
# Enable cross-namespace source references (in bootstrap or gotk-components):
flux install --allow-namespace-cross-references
Complete grade-api Example
# Full directory layout for grade-api with Flux + Helm
clusters/my-cluster/
├── flux-system/ # Bootstrap files (auto-generated)
└── apps/
└── grade-api/
├── namespace.yaml
├── helmrepository.yaml
└── helmrelease.yaml
# apps/grade-api/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: grade-api
labels:
app.kubernetes.io/managed-by: flux
---
# apps/grade-api/helmrepository.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: grade-api
namespace: flux-system
spec:
type: oci
interval: 5m
url: oci://ghcr.io/your-org/charts
secretRef:
name: ghcr-credentials
---
# apps/grade-api/helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: grade-api
namespace: flux-system
spec:
interval: 5m
dependsOn:
- name: postgres # Wait for Postgres HelmRelease to be Ready
chart:
spec:
chart: grade-api
version: ">=1.0.0 <2.0.0"
sourceRef:
kind: HelmRepository
name: grade-api
targetNamespace: grade-api
install:
createNamespace: true
remediation:
retries: 3
upgrade:
remediation:
retries: 3
remediateLastFailure: true
driftDetection:
mode: enabled
valuesFrom:
- kind: ConfigMap
name: grade-api-values
- kind: Secret
name: grade-api-db-secret
optional: true
values:
image:
repository: ghcr.io/your-org/grade-api
tag: latest # Image Updater modifies this
replicaCount: 2
Exercises
Exercise 1 — Deploy with HelmRelease: Create a HelmRelease for
podinfo from its OCI repository (oci://ghcr.io/stefanprodan/charts). Set replicaCount: 2 inline. Watch Flux install it. Then change replicaCount to 3 in Git — watch Flux upgrade the release.
Exercise 2 — Values from ConfigMap: Create a ConfigMap with Helm values for the grade-api chart. Reference it via
valuesFrom. Change a value in the ConfigMap (not the HelmRelease) — verify Flux detects the change and upgrades the release.
Exercise 3 — Intentional Failure: Set an invalid image tag in the HelmRelease values. Watch the install fail, the remediation retries, and the final rollback. Then fix the image tag — watch Flux recover.
Key Takeaways & Next Steps
Key Takeaways:
- HelmRelease is a CRD that manages a Helm release declaratively — you describe the desired state, Flux reconciles it
- Chart sources can be: HelmRepository (HTTP or OCI), GitRepository (chart in repo), or OCIRepository
valuesFromlets you inject values from ConfigMaps and Secrets — keep sensitive values in Secrets, non-sensitive in ConfigMaps- Set
install.remediation.retriesandupgrade.remediation.remediateLastFailure: trueto auto-rollback on failure - OCI registries (GHCR, ECR, ACR) are the modern, immutable way to distribute Helm charts
driftDetection.mode: enabledmakes Flux reconcile manualhelm upgradecommands back to the desired state
Next in This Track
In Part 4: Image Automation & Alerts, we complete the Flux loop — automatically update image tags in Git when new container images are pushed to a registry, and configure Flux to send Slack/Teams notifications on reconciliation events.