Skip to content

Installing Helm Charts with Flux

Flux manages Helm charts through two custom resources: a HelmRepository that points at a chart registry, and a HelmRelease that declares what to install from it. Once those manifests are in Git and wired into a Kustomization, Flux handles installs, upgrades, and drift correction automatically.

Pushing untested Helm values to Git and waiting for Flux to reconcile is slow. Install the chart imperatively first, confirm it works, capture the values, then migrate to Flux.

helm install → test → helm get values → helm uninstall → flux create --export → commit

This is the normal pattern. Flux is for maintaining desired state, not exploratory work.

Add the repository and install the chart into its target namespace:

Terminal window
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespace --version 82.15.0

Verify the pods come up:

Terminal window
kubectl get pods -n monitoring
kubectl get svc -n monitoring

Port-forward Grafana to confirm it’s functional:

Terminal window
kubectl port-forward -n monitoring svc/kube-prometheus-grafana 3000:80

While the trial is running, tune any settings you need — memory limits, resource requests, persistence options. Use helm upgrade to apply them:

Terminal window
helm upgrade kube-prometheus prometheus-community/kube-prometheus-stack \
-n monitoring \
--set prometheus.prometheusSpec.resources.requests.memory=512Mi \
--set prometheus.prometheusSpec.resources.limits.memory=1Gi

Once you’re happy, export the values before you remove the release:

Terminal window
helm get values kube-prometheus -n monitoring -o yaml > /tmp/values.exported.yaml

Flux needs this file. The -a flag includes chart defaults — omit it so the export only captures what you changed.

Terminal window
helm uninstall kube-prometheus -n monitoring

Remove it so Flux can recreate it from Git. If you leave the release in place, Flux will attempt to adopt it — that works when releaseName and namespaces match, but it’s cleaner to start fresh.

Use flux create with --export to generate YAML without applying it:

Terminal window
mkdir -p infrastructure/base/monitoring/
flux create source helm prometheus-community \
--url=https://prometheus-community.github.io/helm-charts \
--interval=1h \
--export > infrastructure/base/monitoring/helmrepository.yaml
flux create helmrelease kube-prometheus \
--source=HelmRepository/prometheus-community \
--chart=kube-prometheus-stack \
--chart-version=82.15.0 \
--release-name=kube-prometheus \
--target-namespace=monitoring \
--interval=5m \
--values=/tmp/values.exported.yaml \
--export > infrastructure/base/monitoring/helmrelease.yaml

The resulting helmrepository.yaml looks like this:

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: flux-system
spec:
interval: 1h0m0s
url: https://prometheus-community.github.io/helm-charts

And helmrelease.yaml:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-prometheus
namespace: flux-system
spec:
chart:
spec:
chart: kube-prometheus-stack
reconcileStrategy: ChartVersion
sourceRef:
kind: HelmRepository
name: prometheus-community
version: 82.15.0
interval: 5m0s
releaseName: kube-prometheus
storageNamespace: monitoring
targetNamespace: monitoring
values:
prometheus:
prometheusSpec:
resources:
limits:
memory: 1Gi
requests:
memory: 512Mi

The HelmRelease lives in flux-system but deploys into monitoring via targetNamespace. The releaseName and storageNamespace fields match what Helm used imperatively, so Flux’s release identity is consistent.

Flux bootstrap only watches clusters/local/. Add a Flux Kustomization there that points at the monitoring manifests:

clusters/local/infrastructure.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./infrastructure/base/monitoring
prune: true

Also add a kustomization.yaml in infrastructure/base/monitoring/ so Kustomize knows which files to include:

infrastructure/base/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- helmrelease.yaml
Terminal window
git add infrastructure/base/monitoring/ clusters/local/infrastructure.yaml
git commit -m "Add kube-prometheus-stack via Flux"
git push

Flux pulls from the remote — it has no access to your local working copy. Trigger an immediate reconcile after pushing:

Terminal window
flux reconcile source git flux-system
flux reconcile kustomization flux-system

The chain: flux-system reads clusters/local/, finds infrastructure.yaml, follows the path to infrastructure/base/monitoring/, and applies the HelmRepository and HelmRelease.

Terminal window
flux get kustomizations
flux get helmreleases -A
kubectl get pods -n monitoring

A healthy install looks like:

NAME REVISION SUSPENDED READY MESSAGE
infrastructure main@sha1:99348924 False True Applied revision: main@sha1:99348924
NAMESPACE NAME REVISION SUSPENDED READY MESSAGE
flux-system kube-prometheus 82.15.0 False True Helm install succeeded for release monitoring/kube-prometheus.v1 with chart kube-prometheus-stack@82.15.0

To force reconciliation of a specific release:

Terminal window
flux reconcile helmrelease kube-prometheus -n flux-system

Flux’s drift correction will revert any manual helm install that conflicts with what’s in Git. If you need to experiment on something Flux already manages, suspend it first:

Terminal window
flux suspend kustomization flux-system
# ... experiment ...
flux resume kustomization flux-system