Three tools claim to install Helm charts on Kubernetes. At first glance, they seem unrelated.
- A Helm-based Operator is built with Operator SDK.
- Flux is a GitOps engine.
- Argo CD is a GitOps platform.
Yet all three can deploy and maintain Helm charts on a Kubernetes cluster. This creates a common question:
If all three can install the same chart, when should I choose one over the others?
The answer is that they solve different problems:
- Helm Operators expose a typed Kubernetes API through a Custom Resource Definition (CRD).
- Flux exposes a Git-driven deployment pipeline.
- Argo CD exposes a Git-driven deployment platform with workflows and visibility.
This guide compares Helm-based operators, Flux's HelmRelease, and Argo CD's Application on the four aspects that actually matter: what triggers the deployment, how drift is corrected, what the tenant-facing CR looks like, and how the three tools layer together in a GitOps strategy. By the end you will know which tool to reach for in any given situation — and why mature platform teams often run all three.
This is part of the Chapter 3 Helm track. Prerequisites: Helm-based operator tutorial and a passing familiarity with Flux or Argo CD (or both).
TL;DR — the decision matrix
| Aspect | Helm-based operator | Flux HelmRelease | Argo CD Application |
|---|---|---|---|
| Trigger | CR create/update (CRD-as-product) | Git change or schedule | Git change or webhook |
| Source of chart | Baked into operator image | HelmRepository / OCIRepository / GitRepository CR | Git repo or Helm repo |
| Drift correction | Yes (dependent watches) | Yes (interval-based) | Yes (auto-sync) |
| Tenant-facing CR | Custom Kind (Memcached) |
Generic HelmRelease |
Generic Application |
| Per-CR validation | OpenAPI v3 on the custom Kind | OpenAPI v3 on HelmRelease (chart-agnostic) |
OpenAPI v3 on Application (chart-agnostic) |
| Multi-cluster | Operator per cluster (sometimes hub-and-spoke) | Native (per-cluster Flux + central image automation) | Native (one Argo CD, many clusters) |
| Sync waves | No (Helm hooks for ordering inside one chart) | Yes (HelmRelease dependsOn) | Yes (Application sync-waves) |
| Rollback heuristics | Manual (helm rollback) |
Yes (test failures trigger rollback) | Yes (automated, with rollback Application) |
| UI | None built-in | None (uses kubectl, Grafana) | Rich web UI |
| RBAC | Standard Kubernetes RBAC on CRDs | Kubernetes RBAC on Flux CRs | Argo CD's own RBAC + Kubernetes |
| Operational overhead | One controller pod + CRDs | Few controllers, no UI, no DB | Heaviest: controller, repo server, Dex, Redis, UI |
| Best for | Platform-team typed APIs | GitOps for entire platform | GitOps with rich UX |
Quick choice heuristics:
- "I want users to
kubectl apply -f memcached.yamland get a Memcached" → Helm-based operator. - "I want Git to be the source of truth for cluster state" → Flux or Argo CD.
- "I want a UI and sync waves out of the box" → Argo CD.
- "I want the lightest GitOps engine with strong source-controller separation" → Flux.
- "I want all of those" → all three layered together (Argo CD/Flux to deploy the operator and its CRs).
What about Crossplane? Crossplane is often raised in this comparison but solves a different problem. A Helm-based operator deploys charts — in-cluster Kubernetes workloads. Crossplane manages external resources — cloud APIs, databases, message buses, anything with an API — through Kubernetes CRDs. If your CR ultimately produces a Deployment/Service/StatefulSet in the same cluster, you want a Helm-based operator. If it provisions an RDS instance or an S3 bucket, you want Crossplane. They coexist happily.
1. Deployment driver: what triggers the install
Helm-based operator. Deployment is driven by Kubernetes CRs. A user creates kind: Memcached, name: my-cache, and the operator runs helm install my-cache. The CR is the trigger; Git is incidental (the CR YAML may live in Git, or may not — the operator does not care).
Flux HelmRelease. Deployment is driven by Git. A HelmRelease CR exists on the cluster, but Flux's HelmController reconciles it against a HelmRepository/OCIRepository/GitRepository source. When the source updates (new chart version, new values file), Flux upgrades the release.
Argo CD Application. Deployment is driven by Git. An Application CR exists on the cluster (or in the Argo CD config), and the Argo CD controller syncs it against a Git repo containing chart + values. Auto-sync (default off) triggers on Git changes; manual sync runs on demand.
The difference matters: a Helm-based operator makes the user-facing API a typed CRD, while Flux/Argo make the user-facing API a Git repository. Different audiences:
- Operator — best when end users are app developers who think in CRs.
- Flux/Argo — best when end users are SREs/platform engineers who think in Git PRs.
Production platforms often have both audiences and run both layers.
2. Drift correction: how fast and how strict
Helm-based operator. When watchDependentResources: true (default), the operator watches every chart-created resource and re-reconciles the CR if any of them changes. Manual kubectl edit on a chart-produced Deployment is reverted within seconds.
Flux HelmRelease. The HelmController re-runs helm upgrade --reuse-values on the configured interval (default 5 min). Drift is corrected on the next interval; not instant. Faster drift detection would require additional watches that Flux does not run by default.
Argo CD Application. The Application controller compares the rendered Git manifests to the cluster state on each sync. With auto-sync on and selfHeal: true, drift is corrected on every sync interval (default 3 min) — fast but not instant. The UI shows drift even when not auto-healing.
| Tool | Drift detection latency | Mechanism |
|---|---|---|
| Helm-based operator | Seconds (watch-based) | controller-runtime informers |
| Flux HelmRelease | Minutes (5 min default) | Periodic resync |
| Argo CD Application | Minutes (3 min default) | Periodic resync + selfHeal |
If sub-minute drift correction is a hard requirement, the operator wins. If 5-minute drift correction is fine and Git is the trigger anyway, Flux/Argo are simpler.
For deeper coverage of the operator-side mechanisms, see drift detection patterns in operators.
3. Tenant-facing CR: typed vs chart-agnostic
The CR a user creates differs in shape:
Helm-based operator (typed CRD):
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 3
image:
tag: "1.6.18"The CR is typed to the operator's Kind. The API surface is the chart's values, exposed as a Kubernetes-native object. Users do not need to know it is a Helm chart underneath.
Flux HelmRelease (chart-agnostic):
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: memcached
spec:
chart:
spec:
chart: memcached
version: "1.6.x"
sourceRef:
kind: HelmRepository
name: bitnami
values:
replicaCount: 3
image:
tag: "1.6.18"The CR carries the chart reference, the version, and a free-form values map. Users must know Helm and the chart's values structure.
Argo CD Application (chart-agnostic):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: memcached
spec:
project: default
source:
repoURL: https://charts.bitnami.com/bitnami
chart: memcached
targetRevision: "1.6.x"
helm:
values: |
replicaCount: 3
image:
tag: "1.6.18"
destination:
namespace: default
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: trueSimilar shape to Flux — chart, version, values block, plus Argo-CD-specific sync policy. Users must know both Helm and Argo CD.
For internal platform teams, the typed operator CR is a better tenant experience. For infrastructure engineers who think in deployments, the chart-agnostic CRs are richer (you can deploy any chart without the platform team adding a new operator).
For an operator's chart authors, the operator's typed CR also gives you OpenAPI validation on the specific shape of the chart's values — Flux/Argo's validation only checks that the values block is valid YAML.
4. GitOps strategy: how the three tools layer together
A common misconception is that GitOps tools and operators solve the same problem. They don't — they answer two different questions, and a real platform uses both. The two extend each other; they do not overlap.
In mature Kubernetes platforms, Helm-based operators, Flux, and Argo CD frequently work together at different layers of the same stack.
Git remains the source of truth, Flux and Argo CD deliver desired state into the cluster, and operators provide domain-specific APIs and lifecycle management for applications and services.
- Flux focuses on lightweight GitOps delivery and continuous synchronization from Git to the cluster.
- Argo CD provides a richer GitOps platform with UI, multi-cluster management, approval workflows, and advanced deployment capabilities.
- Helm-based operators expose domain-specific APIs through Custom Resources and contain application lifecycle knowledge such as upgrades, backups, failover, and validation.
Flux and Argo CD answer how desired state reaches the cluster, while operators answer how that desired state is operated and maintained once it arrives.
Migration paths
From Helm CLI to Helm-based operator
You have helm install foo bar/baz in a CI pipeline. Migrate by:
- Init a Helm-based operator project with that chart (
operator-sdk init --plugins=helm.sdk.operatorframework.io/v1). - Define a CRD shape matching the chart's values.
- Deploy the operator.
- Replace
helm installin the pipeline withkubectl apply -f <cr.yaml>.
The chart and the values do not change; only the deployment mechanism does.
From Helm-based operator to Flux/Argo CD
You have a Helm operator that just deploys one chart per CR. You realise users want GitOps and rich UI. Migrate by:
- Decide if you still need the typed CRD. If yes — keep the operator and add Argo CD/Flux on top to sync CRs from Git.
- If no — replace the operator with
HelmRelease/Applicationresources; export each existing CR's spec to the chart-agnostic format.
Most teams keep the operator for the typed API and add a GitOps engine on top.
From Flux/Argo to Helm-based operator
You have HelmReleases/Applications that all use the same chart with similar values. You realise you want a typed CRD-as-product. Migrate by:
- Build the Helm-based operator with the shared chart.
- Replace each HelmRelease/Application with a typed CR of the operator's Kind.
- Optionally, keep GitOps as the trigger for the CRs (commit the CR to Git, let Argo CD sync the CR onto the cluster, let the operator reconcile the CR to a Helm release).
Common pitfalls
Pitfall 1 — Treating them as competitors. They are not. The operator is a product API; Flux/Argo are delivery pipelines. Most platforms run both.
Pitfall 2 — Building a Helm-based operator when Flux/Argo would do. If the deliverable is "deploy this chart to this namespace with these values", do not invent a new CRD. Use HelmRelease or Application. Reach for the operator only when the typed CRD is the value-add.
Pitfall 3 — Using Argo CD's Application as a typed CRD. The Application CR is generic. If you want users to set "replicas, image, port" without learning Helm or Argo, the operator wraps that shape correctly. Argo Application alone exposes the chart-and-values surface.
Pitfall 4 — Forgetting that an operator is also something Argo/Flux can deploy. The operator itself (its Deployment, CRDs, RBAC) is a regular Kubernetes manifest set. Sync it via GitOps like anything else.
Pitfall 5 — Multi-cluster strategy without thinking. Argo CD is hub-and-spoke; one Argo runs many clusters, and the ApplicationSet generator templates one Application per cluster automatically. Flux is per-cluster by default (every cluster runs its own Flux, with image automation typically centralised). Helm-based operators are per-cluster (one operator deploy per cluster). Pick the topology first, then the tool.
Pitfall 6 — Comparing UIs and forgetting that UIs are not the product. Argo CD's UI is excellent. It also adds operational complexity (it is a stateful component to run, upgrade, secure). Do not pick Argo just for the UI if your team prefers kubectl workflows — Flux or operators are simpler.
Further reading
- Operator SDK: Helm operator tutorial
- Flux: HelmRelease · Sources
- Argo CD: Application · Sync waves and phases
- GitOps: OpenGitOps Principles
- Internal: Helm-based operator tutorial (Part 1) · Helm operator lifecycle, drift and customization (Part 2) · Helm hybrid operator · drift detection patterns
Frequently Asked Questions
1. Are Helm-based operators a kind of GitOps tool?
Not exactly. A Helm-based operator is triggered by Kubernetes CRs, not by Git commits. The CRs themselves may come from Git (via Flux or Argo CD), in which case the operator is a downstream consumer of GitOps. But the operator itself reconciles CRs → Helm releases, not Git → cluster state. GitOps engines reconcile Git → cluster state. The two complement; they do not replace each other.2. When is a Helm-based operator the right choice over Flux/Argo CD?
When the deliverable is a CRD-as-product. If your platform team wants to expose "Memcached" or "Postgres" as a custom resource with its own lifecycle, version, and validation — a Helm-based operator is the right shape. If the deliverable is "deploy this chart to this cluster" — Flux or Argo CD is the right shape. The first is a tenant-facing API; the second is a deployment pipeline.3. Can I use Helm-based operators and Argo CD together?
Yes — and it is a common pattern. Argo CD deploys the operator itself (the controller manager, the CRDs, the RBAC) by syncing from Git. End users then create CRs of the operator's Kinds via their own Git repos, which Argo CD also syncs. The operator reconciles those CRs to Helm releases. Three layers: Argo CD → CRs in Git → operator → chart-rendered resources.4. What does Argo CD''s Application CR do that a Helm operator does not?
Argo CD has opinions about applications: sync waves, sync hooks, automated rollback, drift visualisation, multi-cluster deployment from a central control plane, pull-request previews, RBAC at the Application level. A Helm-based operator is a much thinner abstraction — just "render and apply a chart" — without sync waves, rollback heuristics, or UI.5. What does Flux''s HelmRelease CR do that a Helm operator does not?
Flux has a richer Helm pipeline: source controllers (HelmRepository, OCIRepository) that fetch charts on a schedule, image automation that bumpsimage.tag based on registry scans, decryption of secrets in values via SOPS, multi-tenancy via Kubernetes RBAC on the source CRs. A Helm-based operator does none of this — the chart is baked into the operator image, image tag updates require an operator rebuild, secret values must be wired via Kubernetes-native env-var substitution.6. Can Flux or Argo CD replace a Helm-based operator?
For pure "deploy this chart" cases, yes — Flux/Argo CD do it natively without the operator wrapper. The case where Flux/Argo CD cannot replace the operator: when you want a CRD-as-product API. Flux's HelmRelease and Argo CD's Application are general CRs ("any Helm chart, any values"); they do not give you a typedkind: Memcached with Memcached-specific validation. If that typed API is the deliverable, you need the operator.7. Are there cases where I should use all three?
Yes — platform-engineering teams often do. Argo CD or Flux syncs the operator from Git into the cluster. The operator exposes typed CRDs to tenants. Tenants set their CRs via their own GitOps (also Argo CD/Flux at the tenant level). This stack gives you: GitOps-managed platform, typed platform APIs, and GitOps-managed tenant resources. Three layers, three tools, one platform.8. What is the operational overhead of Argo CD vs Flux vs a Helm-based operator?
Argo CD is the heaviest — it runs an application controller, repo server, API server, Dex (SSO), Redis, and a web UI; you also operate it like a stateful app (backups, upgrades, TLS). Flux is much lighter — a few controllers (source-controller, helm-controller, kustomize-controller, notification-controller) with no UI and no datastore; everything is in CRDs and ConfigMaps. A Helm-based operator is the lightest of all — a single controller-manager pod plus its CRDs. Pick based on whether you need Argo CD's UI and workflows, or whether kubectl + dashboards + Git are enough.9. Should I use Crossplane instead of a Helm-based operator?
They solve different problems. A Helm-based operator deploys charts — Kubernetes workloads rendered from Helm templates. Crossplane manages external resources — cloud APIs, databases, message buses, anything with an API — through Kubernetes CRDs. If your CRD ultimately produces a Deployment/Service/StatefulSet in the same cluster, a Helm-based operator is the right shape. If it provisions an RDS instance, an S3 bucket, or a Cloudflare DNS record, Crossplane is the right shape. They coexist happily in the same cluster.Summary
Helm-based operators, Flux's HelmRelease, and Argo CD's Application all install Helm charts on Kubernetes, but they answer different questions. The operator is a product API — the right shape when you want users to think in CRs of your typed Kind. Flux is a deployment pipeline — the right shape when Git is the source of truth and you want a lean, source-controller-driven engine. Argo CD is an opinionated GitOps platform — the right shape when you want sync waves, rollback, UI, and multi-cluster from a single hub.
Most mature platforms layer them. Argo CD or Flux syncs operators and CRDs from Git into clusters; operators expose typed APIs to tenants; tenants also use GitOps to manage their CRs. Three tools, three layers, one platform.
Pick the tool whose audience and trigger match your need. Do not pick by feature count or UI quality — those matter, but the audience-and-trigger fit is what determines whether the tool actually solves your problem.

