Flux’s HelmRelease Custom Resource Definition (CRD) lets you manage Helm charts declaratively, integrating them directly into your GitOps workflow.

Here’s a HelmRelease manifest for deploying the nginx-ingress chart:

apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
  name: nginx-ingress
  namespace: ingress-nginx
spec:
  interval: 5m
  chart:
    spec:
      chart: ingress-nginx
      version: "4.7.1"
      sourceRef:
        kind: HelmRepository
        name: ingress-nginx
        namespace: flux-system
  install:
    createNamespace: true
  values:
    controller:
      replicaCount: 2
      service:
        type: LoadBalancer

This defines the desired state for the nginx-ingress release: it should be updated every 5 minutes, use version 4.7.1 of the ingress-nginx chart from the ingress-nginx Helm repository (which must be defined elsewhere in Flux), create the ingress-nginx namespace if it doesn’t exist, and set the controller replica count to 2 with a LoadBalancer service type.

Flux continuously reconciles this HelmRelease against the actual state of your cluster. If the nginx-ingress deployment drifts from what’s defined in the HelmRelease, Flux will automatically correct it. This includes installing, upgrading, or uninstalling the Helm release based on changes to the HelmRelease manifest or the underlying chart.

The spec.interval field dictates how often Flux checks for updates. The spec.chart section specifies the Helm chart to deploy, including its name, version, and the repository it’s located in. spec.install contains configuration for the initial installation, such as createNamespace. The spec.values field directly maps to the values.yaml file you’d typically use with helm install or helm upgrade.

You can also manage dependencies between HelmRelease objects using spec.dependsOn. For example, if another HelmRelease named cert-manager must be installed before nginx-ingress, you’d add:

spec:
  dependsOn:
    - name: cert-manager
      namespace: cert-manager
  # ... other fields

This ensures that Flux respects the order of operations, preventing installation failures due to missing dependencies.

A powerful aspect is the ability to define custom resources that HelmRelease can manage, such as Ingress or Service objects, directly within the HelmRelease manifest itself. This allows you to bundle related Kubernetes resources with your Helm chart deployment.

apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
  name: my-app
  namespace: default
spec:
  # ... chart details
  postRenderers:
    - kustomize:
        patches:
          - patch: |-
              apiVersion: networking.k8s.io/v1
              kind: Ingress
              metadata:
                name: my-app-ingress
              spec:
                rules:
                  - host: my-app.example.com
                    http:
                      paths:
                        - path: /
                          pathType: Prefix
                          backend:
                            service:
                              name: my-app-service
                              port:
                                number: 80
            target:
              kind: HelmRelease
              name: my-app

In this snippet, postRenderers allows us to inject or modify resources generated by Helm. Here, we’re using a kustomize patch to define an Ingress resource that targets the my-app Helm release. This means the Ingress will be created or updated when my-app is deployed.

When Flux applies a HelmRelease, it essentially performs a helm upgrade --install under the hood, managed through its own controller. The HelmRelease object itself is the source of truth, and Flux’s helm-controller watches for changes to it. It then translates these changes into Helm operations, fetching the chart, rendering it with the specified values, and applying the resulting Kubernetes manifests.

The most surprising thing is how seamlessly HelmRelease integrates with other Flux components, allowing you to manage not just Helm charts but also Kustomizations and plain Kubernetes manifests from Git, all reconciled by a single GitOps engine. You can even have a Kustomization that points to a directory containing HelmRelease manifests, effectively managing your Helm deployments as part of a larger declarative Git repository.

The next concept to explore is managing secrets for Helm releases, such as private registry credentials or sensitive configuration values.

Want structured learning?

Take the full Helm course →