Helmfile and ArgoCD are better together

Jehoszafat Zimnowoda
Otomi Platform
Published in
3 min readJan 5, 2024
Getting best out of Helmfile and ArgoCD

The art of building platforms involves choosing the right solutions available at the moment, and making intelligent choices as you adapt the platform to face new challenges.

This story is about the evolution of the OSS project called Otomi. My goal is to explain how the engineering team replaced its core deployment component by combining Helmfile together with ArgoCD. Additionally, I want to provide a recipe for applying this approach to already existing Helm releases that are not managed by ArgoCD. Ready? Let’s get started.

Helmfile and ArgoCD in a nutshell

Helmfile is a declarative spec for deploying Helm charts. It is an excelent candidate to be a values rendering engine for Helm releases. It allows you to keep your configuration DRY. Helmfile also orchestrates Helm chart deployments. Helmfile is a command line tool for imperative deployments.

ArgoCD follows the GitOps pattern of using Git repositories as the source of truth for defining the desired application state. It also allows to deploy Helm charts. It implements the Application controller and thus it can reconcile if the deployment does not succeed. ArgoCD is composed by many controllers that support declarative deployments.

Getting the best out of them

Employ Helmfile for generating values tailored to Helm charts. Deploy ArgoCD Applications that leverage values rendered by Helmfile, allowing ArgoCD controllers to handle the deployment of Helm releases.

Evolution rather than revolution

Let’s say you already have many Helmfiles defined, each with dozens of Helm releases. Your customers are using your platform, and you want all deployed Helm releases to be managed by ArgoCD instead of Helmfile. Do you need to start everything from scratch? The good news is you do not have to! Here is the recipe for the evolution of the platform.

  1. List all Helm releases defined in Helmfile(s), e.g.:
helmfile --output=json list
[
{
"name": "gitea",
"namespace": "gitea",
"enabled": true,
"installed": true,
"chart": "charts/gitea",
}
]

2. Next render value files for each Helm release defined in Helmfile(s), e.g.:

helmfile write-values --output-file-template='/tmp/values/{{.Release.Namespace}}-{{.Release.Name}}.yaml'

3. Finally, iterate over the list of Helm releases and generate an ArgoCD Application as follows:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: {{ .Release.namespace }}-{{ .Release.name }}
namespace: argocd
spec:
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
project: default
source:
path: {{ .Release.chart }}
repoURL: 'https://github.com/redkubes/otomi-core.git'
targetRevision: main
helm:
releaseName: release.name
values: |
{{ READ /tmp/values/{{.Release.Namespace}}-{{.Release.Name}}.yaml }}
destination:
server: 'https://kubernetes.default.svc'
namespace: {{ .Release.namespace }}

In particular, the ArgoCD Application for the gitea Helm release that is installed at gitea namespace, will look like the following:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitea-gitea
namespace: argocd
spec:
syncPolicy:
automated:
prune: true
syncOptions: true
syncOptions:
- ServerSideApply=true
project: default
source:
path: charts/gitea
repoURL: 'https://github.com/redkubes/otomi-core.git'
targetRevision: main
helm:
releaseName: gitea
values: |
# values from the /tmp/values/gitea/gitea.yaml file.
destination:
server: 'https://kubernetes.default.svc'
namespace: gitea

Few words about above manifest.

  • All Applicaitons are deployed to argocd namespace. To avoid duplicates the Applicaiton name consists of {{.Release.Namespace}}-{{.Release.Name}} to avoid the duplicates.
  • The syncPolicy. Using prune is safe since your Helm release does not undergo actual changes.
  • The syncOptions needs to enabled to ensure reconciliation process and solve issues with managing dependencies between Helm releases.
  • Moreover the syncOptions requires ServerSideApply=true for mitigating the `Too long: must have at most 262144 bytes` error. Happens with deploying CRDs.
  • Last but not least, if you want ArgoCD to take over the existing Helm releases deployed in the cluster, you need be aware that they already have the app.kubernetes.io/instance={{.Release.Name}} label. Since the Application name is {{.Release.Namespace}}-{{.Release.Name}} the Argocd will try to change this label to app.kubernetes.io/instance={{.Release.Namespace}}-{{.Release.Name}} but it won’t be able to and it will claim that Application as out of sync. In order to mitigate this issue you need to change the default resource tracking label to the annotation in the ArgoCD configuration.

Conclusions

Helmfile addresses the crucial task of generating intricate value files for Helm charts, while the ArgoCD Application Controller guarantees deployment to the Kubernetes cluster. The synergy of these two solutions proves highly potent and prevents the need for duplicating configuration values in the Git repository.

--

--

Jehoszafat Zimnowoda
Otomi Platform

Passionate about computer networks and distributed system. OSS contributor and occasionally technical writer.