# Migrate to the Helm Controller This guide will learn you everything you need to know to be able to migrate from the [Helm Operator](https://github.com/fluxcd/helm-operator) to the [Helm Controller](https://github.com/fluxcd/helm-controller). ## Overview of changes ### Support for Helm v2 dropped The Helm Operator offered support for both Helm v2 and v3, due to Kubernetes client incompatibility issues between the versions. This has blocked the Helm Operator from being able to upgrade to a newer v3 version since the release of `3.2.0`. In combination with the fact that [Helm v2 reaches end of life after November 13, 2020](https://helm.sh/blog/helm-v2-deprecation-timeline/), support for Helm v2 has been dropped. ### Helm and Git repositories, and even Helm charts are now Custom Resources When working with the Helm Operator, you had to mount various files to either make it recognize new (private) Helm repositories or make it gain access to Helm and/or Git repositories. While this approach was declarative, it did not provide a great user experience and was at times hard to set up. By moving this configuration to [`HelmRepository`](../components/source/helmrepositories.md), [`GitRepository`](../components/source/gitrepositories.md), [`Bucket`](../components/source/buckets.md) and [`HelmChart`](../components/source/helmcharts.md) Custom Resources, they can now be declaratively described (including their credentials using references to `Secret` resources), and applied to the cluster. The reconciliation of these resources has been offloaded to a dedicated [Source Controller](../components/source/controller.md), specialized in the acquisition of artifacts from external sources. The result of this all is an easier and more flexible configuration, with much better observability. Failures are traceable to the level of the resource that lead to a failure, and are easier to resolve. As polling intervals can now be configured per resource, you can customize your repository and/or chart configuration to a much finer grain. From a technical perspective, this also means less overhead, as the resources managed by the Source Controller can be shared between multiple `HelmRelease` resources, or even reused by other controllers like the [Kustomize Controller](../components/kustomize/controller.md). ### The `HelmRelease` Custom Resource group domain changed Due to the Helm Controller becoming part of the extensive set of controller components Flux now has, the Custom Resource group domain has changed from `helm.fluxcd.io` to `helm.toolkit.fluxcd.io`. Together with the new API version (`v2beta1` at time of writing), the full `apiVersion` you use in your YAML document becomes `helm.toolkit.fluxcd.io/v2beta1`. ### The API specification changed (quite a lot), for the better While developing the Helm Controller, we were given the chance to rethink what a declarative API for driving automated Helm releases would look like. This has, in short, resulted in the following changes: - Extensive configuration options per Helm action (install, upgrade, test, rollback); this includes things like timeouts, disabling hooks, and ignoring failures for tests. - Strategy-based remediation on failures. This makes it possible, for example, to uninstall a release instead of rolling it back after a failed upgrade. The number of retries or keeping the last failed state when the retries are exhausted is now a configurable option. - Better observability. The `Status` field in the `HelmRelease` provides a much better view of the current state of the release, including dedicated `Ready`, `Released`, `TestSuccess`, and `Remediated` conditions. For a comprehensive overview, see the [API spec changes](#api-spec-changes). ### Helm storage drift detection no longer relies on dry-runs The Helm Controller no longer uses dry-runs as a way to detect mutations to the Helm storage. Instead, it uses a simpler model of bookkeeping based on the observed state and revisions. This has resulted in much better performance, a lower memory and CPU footprint, and more reliable drift detection. ### No longer supports [Helm downloader plugins](https://helm.sh/docs/topics/plugins/#downloader-plugins) We have reduced our usage of Helm packages to a bare minimum (that being: as much as we need to be able to work with chart repositories and charts), and are avoiding shell outs as much as we can. Given the latter, and the fact that Helm (downloader) plugins work based on shelling out to another command and/or binary, support for this had to be dropped. We are aware some of our users are using this functionality to be able to retrieve charts from S3 or GCS. The Source Controller already has support for S3 storage compatible buckets ([this includes GCS](https://cloud.google.com/storage/docs/interoperability)), and we hope to extend this support in the foreseeable future to be on par with the plugins that offered support for these Helm repository types. ### Values from `ConfigMap` and `Secret` resources in other namespaces are no longer supported Support for values references to `ConfigMap` and `Secret` resources in other namespaces than the namespace of the `HelmRelease` has been dropped, as this allowed information from other namespaces to leak into the composed values for the Helm release. ### Values from external source references (URLs) are no longer supported We initially introduced this feature to support alternative (production focused) `values.yaml` files that sometimes come with charts. It was also used by users to use generic and/or dynamic `values.yaml` files in their `HelmRelease` resources. The former can now be achieved by defining a [`ValuesFile` overwrite in the `HelmChartTemplateSpec`](#chart-file-references), which will make the Source Controller look for the referenced file in the chart, and overwrite the default values with the contents from that file. Support for the latter use has been dropped, as it goes against the principles of GitOps and declarative configuration. You can not reliably restore the cluster state from a Git repository if the configuration of a service relies on some URL being available. Getting similar behaviour is still possible [using a workaround that makes use of a `CronJob` to download the contents of the external URL on an interval](#external-source-references). ### You can now merge single values at a given path There was a long outstanding request for the Helm Operator to support merging single values at a given path. With the Helm Controller this now possible by defining a [`targetPath` in the `ValuesReference`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.ValuesReference), which supports the same formatting as you would supply as an argument to the `helm` binary using `--set [path]=[value]`. In addition to this, the referred value can contain the same value formats (e.g. `{a,b,c}` for a list). You can read more about the available formats and limitations in the [Helm documentation](https://helm.sh/docs/intro/using_helm/#the-format-and-limitations-of---set). ### Support added for depends-on relationships We have added support for depends-on relationships to install `HelmRelease` resources in a given order; for example, because a chart relies on the presence of a Custom Resource Definition installed by another `HelmRelease` resource. Entries defined in the `spec.dependsOn` list of the `HelmRelease` must be in a `Ready` state before the Helm Controller proceeds with installation and/or upgrade actions. Note that this does not account for upgrade ordering. Kubernetes only allows applying one resource (`HelmRelease` in this case) at a time, so there is no way for the controller to know when a dependency `HelmRelease` may be updated. Also, circular dependencies between `HelmRelease` resources must be avoided, otherwise the interdependent `HelmRelease` resources will never be reconciled. ### You can now suspend a HelmRelease There is a new `spec.suspend` field, that if set to `true` causes the Helm Controller to skip reconciliation for the resource. This can be utilized to e.g. temporarily ignore chart changes, and prevent a Helm release from getting upgraded. ### Helm releases can target another cluster We have added support for making Helm releases to other clusters. If the `spec.kubeConfig` field in the `HelmRelease` is set, Helm actions will run against the default cluster specified in that KubeConfig instead of the local cluster that is responsible for the reconciliation of the `HelmRelease`. The Helm storage is stored on the remote cluster in a namespace that equals to the namespace of the `HelmRelease`, the release itself is made in either this namespace, or the configured `spec.targetNamespace`. In any case, both are expected to exist, and can for example be created using the [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/) which has the same cross-cluster support. Other references to Kubernetes resources in the `HelmRelease`, like `ValuesReference` resources, are expected to exist on the reconciling cluster. ### Added support for notifications and webhooks Sending notifications and/or alerts to Slack, Microsoft Teams, Discord, or Rocker is now possible using the [Notification Controller](../components/notification/controller.md), [`Provider` Custom Resources](../components/notification/provider.md) and [`Alert` Custom Resources](../components/notification/alert.md). It does not stop there, using [`Receiver` Custom Resources](../components/notification/receiver.md) you can trigger **push based** reconciliations from Harbor, GitHub, GitLab, BitBucket or your CI system by making use of the webhook endpoint the resource creates. ### Introduction of the `flux` CLI to create and/or generate Custom Resources With the new [`flux` CLI](../cmd/flux.md) it is now possible to create and/or generate the Custom Resources mentioned earlier. To generate the YAML for a `HelmRepository` and `HelmRelease` resource, you can for example run: ```console $ flux create source helm podinfo \ --url=https://stefanprodan.github.io/podinfo \ --interval=10m \ --export --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: podinfo namespace: flux-system spec: interval: 10m0s url: https://stefanprodan.github.io/podinfo $ flux create helmrelease podinfo \ --interval=10m \ --source=HelmRepository/podinfo \ --chart=podinfo \ --chart-version=">4.0.0" \ --export --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: podinfo namespace: flux-system spec: chart: spec: chart: podinfo sourceRef: kind: HelmRepository name: podinfo version: '>4.0.0' interval: 10m0s ``` ## API spec changes The following is an overview of changes to the API spec, including behavioral changes compared to how the Helm Operator performs actions. For a full overview of the new API spec, consult the [API spec documentation](../components/helm/helmreleases.md#specification). ### Defining the Helm chart #### Helm repository For the Helm Operator, you used to configure a chart from a Helm repository as follows: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: my-release namespace: default spec: chart: # The repository URL repository: https://charts.example.com # The name of the chart (without an alias) name: my-chart # The SemVer version of the chart version: 1.2.3 ``` With the Helm Controller, you now create a `HelmRepository` resource in addition to the `HelmRelease` you would normally create (for all available fields, consult the [Source API reference](../components/source/api.md#source.toolkit.fluxcd.io/v1beta1.HelmRepository)): ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: my-repository namespace: default spec: # The interval at wich to check the upstream for updates interval: 10m # The repository URL, a valid URL contains at least a protocol and host url: https://chart.example.com ``` If you make use of a private Helm repository, instead of configuring the credentials by mounting a `repositories.yaml` file, you can now configure the HTTP/S basic auth and/or TLS credentials by referring to a `Secret` in the same namespace as the `HelmRepository`: ```yaml --- apiVersion: v1 kind: Secret metadata: name: my-repository-creds namespace: default data: # HTTP/S basic auth credentials username: password: # TLS credentials (certFile and keyFile, and/or caCert) certFile: keyFile: caCert: --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: my-repository namespace: default spec: # ...omitted for brevity secretRef: name: my-repository-creds ``` In the `HelmRelease`, you then use a reference to the `HelmRepository` resource in the `spec.chart.spec` (for all available fields, consult the [Helm API reference](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplate)): ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # The interval at which to reconcile the Helm release interval: 10m chart: spec: # The name of the chart as made available by the HelmRepository # (without any aliases) chart: my-chart # A fixed SemVer, or any SemVer range # (i.e. >=4.0.0 <5.0.0) version: 1.2.3 # The reference to the HelmRepository sourceRef: kind: HelmRepository name: my-repository # Optional, defaults to the namespace of the HelmRelease namespace: default ``` The `spec.chart.spec` values are used by the Helm Controller as a template to create a new `HelmChart` resource in the same namespace as the `sourceRef`, to be reconciled by the Source Controller. The Helm Controller watches `HelmChart` resources for (revision) changes, and performs an installation or upgrade when it notices a change. #### Git repository For the Helm Operator, you used to configure a chart from a Git repository as follows: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: my-release namespace: default spec: chart: # The URL of the Git repository git: https://example.com/org/repo # The Git branch (or other Git reference) ref: master # The path of the chart relative to the repository root path: ./charts/my-chart ``` With the Helm Controller, you create a `GitRepository` resource in addition to the `HelmRelease` you would normally create (for all available fields, consult the [Source API reference](../components/source/api.md#source.toolkit.fluxcd.io/v1beta1.GitRepository): ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: my-repository namespace: default spec: # The interval at which to check the upstream for updates interval: 10m # The repository URL, can be a HTTP/S or SSH address url: https://example.com/org/repo # The Git reference to checkout and monitor for changes # (defaults to master) # For all available options, see: # https://toolkit.fluxcd.io/components/source/api/#source.toolkit.fluxcd.io/v1beta1.GitRepositoryRef ref: branch: master ``` If you make use of a private Git repository, instead of configuring the credentials by mounting a private key and making changes to the `known_hosts` file, you can now configure the credentials for both HTTP/S and SSH by referring to a `Secret` in the same namespace as the `GitRepository`: ```yaml --- apiVersion: v1 kind: Secret metadata: name: my-repository-creds namespace: default data: # HTTP/S basic auth credentials username: password: # SSH credentials identity: identity.pub: known_hosts: --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: my-repository namespace: default spec: # ...omitted for brevity secretRef: name: my-repository-creds ``` In the `HelmRelease`, you then use a reference to the `GitRepository` resource in the `spec.chart.spec` (for all available fields, consult the [Helm API reference](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.HelmChartTemplate)): ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # The interval at which to reconcile the Helm release interval: 10m chart: spec: # The path of the chart relative to the repository root chart: ./charts/my-chart # The reference to the GitRepository sourceRef: kind: GitRepository name: my-repository # Optional, defaults to the namespace of the HelmRelease namespace: default ``` The `spec.chart.spec` values are used by the Helm Controller as a template to create a new `HelmChart` resource in the same namespace as the `sourceRef`, to be reconciled by the Source Controller. The Helm Controller watches `HelmChart` resources for (revision) changes, and performs an installation or upgrade when it notices a change. ### Defining values #### Inlined values Inlined values (defined in the `spec.values` of the `HelmRelease`) still work as with the Helm operator. It represents a YAML map as you would put in a file and supply to `helm` with `-f values.yaml`, but inlined into the `HelmRelease` manifest: ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity values: foo: value1 bar: baz: value2 oof: - item1 - item2 ``` #### Values from sources As described in the [overview of changes](#overview-of-changes), there have been multiple changes to the way you can refer to values from sources (like `ConfigMap` and `Secret` references), including the [drop of support for external source (URL) references](#values-from-external-source-references-urls-are-no-longer-supported) and [added support for merging single values at a specific path](#you-can-now-merge-single-values-at-a-given-path). Values are still merged in the order given, with later values overwriting earlier. The values from sources always have a lower priority than the values inlined in the `HelmRelease` via the `spec.values` key. ##### `ConfigMap` and `Secret` references `ConfigMap` and `Secret` references used to be defined as follows: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity valuesFrom: - configMapKeyRef: name: my-config-values namespace: my-ns key: values.yaml optional: false - secretKeyRef: name: my-secret-values namespace: my-ns key: values.yaml optional: true ``` In the new API spec the individual `configMapKeyRef` and `secretKeyRef` objects are bundled into a single [`ValuesReference`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.ValuesReference) which [does no longer allow refering to resources in other namespaces](#values-from-external-source-references-urls-are-no-longer-supported): ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity valuesFrom: - kind: ConfigMap name: my-config-values valuesKey: values.yaml optional: false - kind: Secret name: my-secret-values valuesKey: values.yaml optional: true ``` Another thing to take note of is that the behavior for values references marked as `optional` has changed. When set, a "not found" error for the values reference is ignored, but any `valuesKey`, `targetPath` or transient error will still result in a reconciliation failure. ##### Chart file references With the Helm Operator it was possible to refer to an alternative values file (for e.g. production usage) in the directory of a chart from a Git repository: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity valuesFrom: # Values file to merge in, # expected to be a relative path in the chart directory - chartFileRef: path: values-prod.yaml ``` With the Helm Controller, this declaration has moved to the `spec.chart.spec`, and the feature is no longer limited to charts from a Git repository: ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity chart: spec: chart: my-chart version: 1.2.3 # Alternative values file to use as the default values, # expected to be a relative path in the sourceRef valuesFile: values-prod.yaml sourceRef: kind: HelmRepository name: my-repository ``` When the `valuesFile` is defined, the chart will be (re)packaged with the values from the referenced file as the default values. Note that this behavior is different from the Helm Operator and requires a full set of alternative values, as the referenced values are no longer merged with the default values. ##### External source references While [the support for external source references has been dropped](#values-from-external-source-references-urls-are-no-longer-supported), it is possible to work around this limitation by creating a `CronJob` that periodically fetches the values from an external URL and saves them to a `ConfigMap` or `Secret` resource. First, create a `ServiceAccount`, `Role` and `RoleBinding` capable of updating a limited set of `ConfigMap` resources: ```yaml --- apiVersion: v1 kind: ServiceAccount metadata: name: values-fetcher namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: configmap-updater namespace: default rules: - apiGroups: [""] resources: ["configmaps"] # ResourceNames limits the access of the role to # a defined set of ConfigMap resources resourceNames: ["my-external-values"] verbs: ["patch", "get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: update-values-configmaps namespace: default subjects: - kind: ServiceAccount name: values-fetcher namespace: default roleRef: kind: Role name: configmap-updater apiGroup: rbac.authorization.k8s.io ``` As `resourceNames` scoping in the `Role` [does not allow restricting `create` requests](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources), we need to create empty placeholder(s) for the `ConfigMap` resource(s) that will hold the fetched values: ```yaml --- apiVersion: v1 kind: ConfigMap metadata: name: my-external-values namespace: default data: {} ``` Lastly, create a `CronJob` that uses the `ServiceAccount` defined above, fetches the external values on an interval, and applies them to the `ConfigMap`: ```yaml --- apiVersion: batch/v1beta1 kind: CronJob metadata: name: fetch-external-values spec: concurrencyPolicy: Forbid schedule: "*/5 * * * *" successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: template: spec: serviceAccountName: values-fetcher containers: - name: kubectl image: bitnami/kubectl:1.19 volumeMounts: - mountPath: /tmp name: tmp-volume command: - sh - -c args: - >- curl -f -# https://example.com/path/to/values.yaml -o /tmp/values.yaml && kubectl create configmap my-external-values --from-file=/tmp/values.yaml -oyaml --dry-run=client | kubectl apply -f - volumes: - name: tmp-volume emptyDir: medium: Memory restartPolicy: OnFailure ``` You can now refer to the `my-external-values` `ConfigMap` resource in your `HelmRelease`: ```yaml --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity valuesFrom: - kind: ConfigMap name: my-external-values ``` ### Defining release options With the Helm Operator the release options used to be configured in the `spec` of the `HelmRelease` and applied to both Helm install and upgrade actions. This has changed for the Helm Controller, where some defaults can be defined in the [`spec`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.HelmReleaseSpec), but specific action configurations and overwrites for the defaults can be defined in the [`spec.install`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Install), [`spec.upgrade`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Upgrade) and [`spec.test`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Test) sections of the `HelmRelease`. ### Defining a rollback / uninstall configuration With the Helm Operator, uninstalling a release after an installation failure was done automatically, and rolling back from a faulty upgrade and configuring options like retries was done as follows: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity rollback: enable: true retries: true maxRetries: 5 disableHooks: false force: false recreate: false timeout: 300 ``` The Helm Controller offers an extensive set of configuration options to remediate when a Helm release fails, using [`spec.install.remediate`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.InstallRemediation), [`spec.upgrade.remediate`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.UpgradeRemediation), [`spec.rollback`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Rollback) and [`spec.uninstall`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Uninstall). Some of the new features include the option to remediate with an uninstall after an upgrade failure, and the option to keep a failed release for debugging purposes when it has run out of retries. #### Automated uninstalls The configuration below mimics the uninstall behavior of the Helm Operator (for all available fields, consult the [`InstallRemediation`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.InstallRemediation) and [`Uninstall`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Uninstall) API references): ```yaml apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity install: # Remediation configuration for when the Helm install # (or sequent Helm test) action fails remediation: # Number of retries that should be attempted on failures before # bailing, a negative integer equals to unlimited retries retries: -1 # Configuration options for the Helm uninstall action uninstall: timeout: 5m disableHooks: false keepHistory: false ``` #### Automated rollbacks The configuration below shows an automated rollback configuration that equals [the configuration for the Helm Operator showed above](#defining-a-rollback-uninstall-configuration) (for all available fields, consult the [`UpgradeRemediation`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.UpgradeRemediation) and [`Rollback`](../components/helm/api.md#helm.toolkit.fluxcd.io/v2beta1.Rollback) API references): ```yaml apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: my-release namespace: default spec: # ...omitted for brevity upgrade: # Remediaton configuration for when an Helm upgrade action fails remediation: # Amount of retries to attempt after a failure, # setting this to 0 means no remedation will be # attempted retries: 5 # Configuration options for the Helm rollback action rollback: timeout: 5m disableWait: false disableHooks: false recreate: false force: false cleanupOnFail: false ``` ## Migration strategy Due to the high number of changes to the API spec, there are no detailed instructions available to provide a simple migration path. But there is a [simple procedure to follow](#steps), which combined with the detailed list of [API spec changes](#api-spec-changes) should make the migration path relatively easy. Here are some things to know: * The Helm Controller will ignore the old custom resources (and the Helm Operator will ignore the new resources). * Deleting a resource while the corresponding controller is running will result in the Helm release also being deleted. * Deleting a `CustomResourceDefinition` will also delete all custom resources of that kind. * If both the Helm Controller and Helm Operator are running, and both a new and old custom resources define a release, they will fight over the release. * The Helm Controller will always perform an upgrade the first time it encounters a new `HelmRelease` for an existing release; this is [due to the changes to release mechanics and bookkeeping](#helm-storage-drift-detection-no-longer-relies-on-dry-runs). The safest way to upgrade is to avoid deletions and fights by stopping the Helm Operator. Once the operator is not running, it is safe to deploy the Helm Controller (e.g., by following the [Get Started guide](../get-started/index.md), [utilizing `flux install`](../cmd/flux_install.md), or using the manifests from the [release page](https://github.com/fluxcd/helm-controller/releases)), and start replacing the old resources with new resources. You can keep the old resources around during this process, since the Helm Controller will ignore them. ### Steps The recommended migration steps for a single `HelmRelease` are as follows: 1. Ensure the Helm Operator is not running, as otherwise the Helm Controller and Helm Operator will fight over the release. 1. Create a [`GitRepository` or `HelmRepository` resource for the `HelmRelease`](#defining-the-helm-chart), including any `Secret` that may be required to access the source. Note that it is possible for multiple `HelmRelease` resources to share a `GitRepository` or `HelmRepository` resource. 1. Create a new `HelmRelease` resource ([with the `helm.toolkit.fluxcd.io` group domain](#the-helmrelease-custom-resource-group-domain-changed)), define the `spec.releaseName` (and `spec.targetNamespace` if applicable) to match that of the existing release, and rewrite the configuration to adhere to the [API spec changes](#api-spec-changes). 1. Confirm the Helm Controller successfully upgrades the release. ### Example As a full example, this is an old resource: ```yaml --- apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: podinfo namespace: default spec: chart: repository: https://stefanprodan.github.io/podinfo name: podinfo version: 5.0.3 values: replicaCount: 1 ``` The custom resources for the Helm Controller would be: ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: HelmRepository metadata: name: podinfo namespace: default spec: interval: 10m url: https://stefanprodan.github.io/podinfo --- apiVersion: helm.toolkit.fluxcd.io/v2beta1 kind: HelmRelease metadata: name: podinfo namespace: default spec: interval: 5m releaseName: default-podinfo chart: spec: chart: podinfo version: 5.0.3 sourceRef: kind: HelmRepository name: podinfo interval: 10m values: replicaCount: 1 ``` ### Migrating gradually Gradually migrating to the Helm Controller is possible by scaling down the Helm Operator while you move over resources, and scaling it up again once you have migrated some of the releases to the Helm Controller. While doing this, make sure that once you scale up the Helm Operator again, there are no old and new `HelmRelease` resources pointing towards the same release, as they will fight over the release. ### Deleting old resources Once you have migrated all your `HelmRelease` resources to the Helm Controller. You can remove all of the old resources by removing the old Custom Resource Definition. ```sh kubectl delete crd helm.fluxcd.io ``` ## Frequently Asked Questions ### Are automated image updates supported? Not yet, but the feature is under active development. See the [image update feature parity section on the roadmap](https://toolkit.fluxcd.io/roadmap/#flux-image-update-feature-parity) for updates on this topic. ### How do I automatically apply my `HelmRelease` resources to the cluster? If you are currently a Flux v1 user, you can commit the `HelmRelease` resources to Git, and Flux will automatically apply them to the cluster like any other resource. It does however not support automated image updates for Helm Controller resources. If you are not a Flux v1 user or want to fully migrate to Flux v2, the [Kustomize Controller](https://toolkit.fluxcd.io/components/kustomize/controller/) will serve your needs. ### I am still running Helm v2, what is the right upgrade path for me? Migrate your Helm v2 releases to v3 using [the Helm Operator's migration feature](https://docs.fluxcd.io/projects/helm-operator/en/stable/helmrelease-guide/release-configuration/#migrating-from-helm-v2-to-v3), or make use of the [`helm-2to3`](https://github.com/helm/helm-2to3) plugin directly, before continuing following the [migration steps](#steps). ### Is the Helm Controller ready for production? Probably, but with some side notes: 1. It is still under active development, and while our focus has been to stabilize the API as much as we can during the first development phase, we do not guarantee there will not be any breaking changes before we reach General Availability. We are however committed to provide [conversion webhooks](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion) for upcoming API versions. 1. There may be (internal) behavioral changes in upcoming releases, but they should be aimed at further stabilizing the Helm Controller itself, solving edge case issues, providing better logging, observability, and/or other improvements. ### I have another question Given the amount of changes, it is quite possible that this document did not provide you with a clear answer for you specific setup. If this applies to you, do not hestitate to ask for help in the [GitHub Discussions](https://github.com/fluxcd/flux2/discussions/new?category_id=31999889) or on the [`#flux` CNCF Slack channel](https://slack.cncf.io)!