Operators vs Helm vs Plain Manifests
Summary
- Plain manifests, Helm, and operators are three points on a spectrum from static declaration to templated packaging to active, ongoing automation.
- Insight: The real axis is day-2 behavior. Manifests and Helm describe desired state at apply time; an operator keeps enforcing it continuously.
- Key Concepts: declarative manifests, Helm templating/release, operator (CRD + controller), day-1 vs day-2, reconciliation, complexity cost.
- When to Use: Manifests for simple static config, Helm for packaging and distribution, operators for stateful lifecycle automation.
- Limitations/Trade-offs: Each step up the spectrum adds power and adds ownership cost; over-choosing an operator is a common form of over-engineering.
- Related Topics: The operator pattern, running operators in production, controllers and reconciliation.
Foundations
All three approaches ultimately produce Kubernetes API objects. The difference is who generates them, when, and whether anything watches them afterward.
Plain manifests are the objects themselves, written as YAML and applied directly. What you write is what exists.
Helm adds templating and packaging on top. A chart is a parameterized bundle of manifests plus a release lifecycle, so one chart installs, upgrades, and rolls back a set of objects.
Operators add a running controller. Beyond producing objects once, an operator continuously reconciles a high-level custom resource, taking corrective action over the application's whole life.
The mental model is a spectrum of activity. Manifests are inert once applied; Helm is active only during install and upgrade commands; an operator is active forever.
That framing answers most choices: ask how much ongoing, application-aware automation the workload actually needs.
Mechanics & Interactions
With plain manifests, kubectl apply sends objects to the API server and built-in controllers take it from there. There is no extra layer of your own.
Kustomize fits here too: it overlays and patches base manifests to produce environment-specific variants, still without templating or a runtime component.
Helm renders templates locally into manifests, then applies them and records a release. Upgrades diff against the last release; helm rollback reverts to a prior one.
Helm's logic runs only when you invoke it. Between commands, nothing Helm-specific watches your objects; the built-in controllers do the reconciling.
An operator, by contrast, installs a CRD and a controller Pod. You then create a custom resource, and the controller reconciles it on every change and on a timer, indefinitely.
This is the decisive interaction difference: an operator responds to drift and to application events on its own, while Helm and manifests require a human or a GitOps tool to re-apply.
GitOps tools like Argo CD or Flux sit alongside all three. They continuously apply manifests, Helm charts, or operator custom resources from Git, adding reconciliation of the desired manifests without adding application-specific logic.
So GitOps gives you drift correction of Kubernetes objects; an operator gives you drift correction plus application-specific procedures like failover and backup.
Advanced Considerations & Applications
Choose plain manifests when the workload is simple and static: a stateless Deployment, a Service, a ConfigMap. Adding Helm or an operator here is needless machinery.
Choose Helm when you package software for reuse or distribution, need parameterization across environments, or want release-based upgrade and rollback semantics.
Helm and Kustomize are not mutually exclusive with GitOps; both are commonly the templating layer that Argo CD or Flux applies.
Choose an operator when the application needs procedural, stateful day-2 automation that no template can express: coordinated upgrades, failover, backup and restore, or scaling with app-specific ordering.
There is also a hybrid: many operators are themselves installed via a Helm chart, and some Helm charts exist mainly to deploy an operator. The layers compose.
Weigh the cost honestly. An operator is a long-lived, privileged program you must secure, upgrade, and put on-call for, as covered in Using Operators in Production.
A useful rule: use the least active mechanism that safely meets the need. Escalate from manifests to Helm to an operator only when the workload's day-2 requirements demand it.
For most application teams that means manifests or Helm for their own services, and adopting operators mainly for infrastructure like databases, certificates, and monitoring.
Common Misconceptions
"Helm is an operator." No. Helm templates and applies manifests on command; it does not run a controller that watches and reconciles your objects afterward.
"Operators replace Helm." They solve different problems and often coexist; operators are frequently packaged and installed as Helm charts.
"GitOps makes operators unnecessary." GitOps reconciles Kubernetes objects toward Git. It does not know how to fail over a database or rotate credentials the way an app-specific operator does.
"An operator is always more capable, so prefer it." Capability comes with standing privilege and ownership cost. For static workloads it is pure overhead.
"Kustomize competes with Helm." They overlap but differ: Kustomize patches base YAML with no templating language, while Helm templates and manages releases. Teams pick by preference or use both.
FAQs
When are plain manifests enough? For simple, static, mostly-stateless workloads where built-in controllers already provide the reconciliation you need.
What does Helm add over manifests? Parameterized templating, packaging for reuse, and release-based install, upgrade, and rollback of a group of objects.
What does an operator add over Helm? A continuously running controller that performs application-specific day-2 operations and corrects drift on its own.
Can I use them together? Yes. Operators are often installed via Helm, and GitOps tools continuously apply manifests, charts, or operator custom resources.
Is GitOps a fourth option? It is orthogonal: a delivery mechanism that continuously applies any of the three from Git, adding manifest-level drift correction.
How do I avoid over-engineering? Use the least active mechanism that meets the day-2 need, and reserve operators for stateful, procedure-heavy systems.
Related
- The Operator Pattern Explained
- Using Operators in Production
- How Controllers Work (controller-runtime)
- Extending Kubernetes: The Big Idea
- CRDs & Operators Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).