Kubernetes Operators get proposed for problems that a Deployment and a ConfigMap would solve just as well, and platform teams that build one anyway end up maintaining a small piece of custom software — with its own bugs, its own upgrade path, its own on-call burden — that a simpler primitive would have avoided entirely. The operator pattern is genuinely valuable for a specific class of problem. It’s also one of the most over-applied patterns in the Kubernetes ecosystem.
What an Operator Actually Is
An Operator is application-specific operational knowledge, encoded as software, watching a Custom Resource Definition (CRD) and reconciling the cluster’s actual state toward whatever the CRD declares. The pattern extends Kubernetes’s own core reconciliation loop — the same mechanism that keeps a Deployment’s replica count matching its spec — to domain-specific logic a built-in controller doesn’t know how to perform.
The canonical example is a database operator. Kubernetes natively knows how to run a StatefulSet, but it doesn’t know how to safely fail over a Postgres primary to a replica, how to take a consistent backup before a destructive schema migration, or how to scale read replicas based on query load. A well-built Postgres operator (Zalando’s, CloudNativePG, or a vendor-managed equivalent) encodes that domain expertise so kubectl apply -f postgres-cluster.yaml produces a correctly configured, self-healing database cluster instead of a StatefulSet that happens to run Postgres binaries without any of the operational intelligence around it.
The Test: Does Day-Two Operations Require Domain Knowledge Kubernetes Doesn’t Have?
The dividing line isn’t “is this workload complex” — it’s whether ongoing operations (not just initial deployment) require domain-specific logic beyond what Kubernetes’s built-in controllers already provide.
Good operator candidates:
- Stateful data systems with non-trivial failover, backup, or scaling logic (databases, message queues, search clusters)
- Systems with a multi-step, order-dependent upgrade process that must coordinate across multiple components (a distributed system where nodes must upgrade in a specific sequence)
- Workloads that need to react to external events or metrics that aren’t native Kubernetes signals (scaling based on a queue depth from an external message broker, for instance)
- Anything where “restore from backup” or “promote a replica” is a multi-step runbook today that an operator could encode and automate
Bad operator candidates (a Deployment plus a Helm chart is sufficient):
- Stateless web services and APIs, regardless of how many of them you’re running
- Applications where the “custom logic” is really just templated YAML with a few variables — that’s what Helm charts and Kustomize overlays are for, not a CRD and a controller
- One-off internal tools where the engineering cost of building and maintaining an operator will never be recovered by the operational time it saves
- Anything where the team’s real goal is “make deployment configuration reusable,” which is a templating problem, not a reconciliation problem
The Maintenance Cost Nobody Prices In Up Front
Building an operator means committing to maintaining a piece of software with its own release cycle, its own testing burden (operator logic bugs can corrupt or destroy the very state they’re supposed to protect — a broken database operator is a worse failure mode than a broken Helm chart), and its own compatibility matrix against Kubernetes API version changes. The Operator SDK and Kubebuilder tooling have made building an operator dramatically easier than in Kubernetes’s early years, but “easier to build” isn’t the same as “free to maintain.” The cost that surprises teams isn’t the initial build — it’s the years afterward: tracking upstream Kubernetes API deprecations, re-testing the reconciliation logic against every new cluster version, and carrying the on-call pager for a bespoke component that can silently corrupt the very state it manages if a controller bug slips through a release. That recurring tax is invisible at the moment the operator is first written, which is exactly why it gets underestimated in the decision to build one.
A platform team that builds a custom operator for a workload that didn’t strictly need one is signing up to own that maintenance burden indefinitely, for a problem a Helm chart with a well-designed values.yaml would have solved with dramatically less ongoing cost. The rule of thumb worth applying: if the operational logic you’re trying to encode fits comfortably in a runbook a human follows occasionally, it’s very likely templating, not reconciliation, and doesn’t need a controller.
The Operator Maturity Model: Not All Operators Do the Same Job
The Operator Framework community has settled on a rough five-level maturity model that’s useful for evaluating both third-party operators and your own team’s ambitions, if you do end up building one. Level one operators handle basic install and configuration only. Level two adds seamless upgrades. Level three adds full lifecycle management — backup, restore, failure recovery. Level four adds deep insight through metrics, alerts, and log processing specific to the managed application. Level five adds full auto-pilot behavior — horizontal and vertical scaling, auto-tuning, and abnormality detection without human intervention.
Most operators in production, including many widely-used database operators, sit at level two or three — they handle install, upgrade, and basic lifecycle operations well, but stop short of the auto-pilot behavior implied by “operator” in its most ambitious sense. This matters when evaluating a third-party operator: a Postgres operator that claims to “manage your database” but only handles levels one and two still requires a human to intervene for failover testing, capacity planning, and abnormal-condition response — which is fine, as long as the platform team’s expectations match what the operator actually automates rather than what the pattern theoretically promises.
Testing an Operator Before It Manages Production State
Because a buggy operator can actively damage the state it manages — a reconciliation bug that decides a healthy replica needs to be recreated, or a backup routine that silently fails to capture a consistent snapshot — the testing bar for operator logic needs to be meaningfully higher than for a typical stateless service. This applies whether you’re building a custom operator or evaluating a third-party one to run in production.
Practical minimums worth establishing before trusting an operator with real data: chaos-testing the operator’s failover logic in a non-production cluster that mirrors production topology, verifying backup-and-restore actually round-trips (a backup that’s never been restored is not a tested backup), and reviewing the operator’s own RBAC permissions to confirm it can’t do more damage than its stated job requires if its logic misfires. Teams that skip this step because “it’s a popular, well-maintained open-source operator” are trusting a stranger’s testing coverage against their own specific data and failure modes — usually fine, but worth verifying rather than assuming.
Use Someone Else’s Operator Before Building Your Own
For the genuine operator use cases (databases, message queues, search infrastructure, cert-management, ingress controllers), a mature ecosystem of existing operators already exists — CloudNativePG and Zalando’s operator for Postgres, the Strimzi operator for Kafka, the Elastic Cloud on Kubernetes (ECK) operator for Elasticsearch, cert-manager for certificate lifecycle. These are maintained by teams whose full-time job is operating that specific system correctly, tested against far more edge cases than an internal platform team building a first operator will encounter.
Building a custom operator should be reserved for internal, organization-specific operational logic that genuinely has no existing open-source or vendor equivalent — not as the default first move for any workload that looks “stateful” or “complex.”
What This Means for a Platform Team’s Roadmap
When a request comes in for “can we build an operator for X,” the right first question is whether an existing operator already solves it, the second is whether the actual requirement is reconciliation (ongoing, event-driven operational logic) or templating (making initial deployment configuration reusable), and only if both answers point toward “genuinely custom, genuinely reconciliation-shaped” does building one become the right call. Most requests resolve at the first or second question.
Related Reading
- For the storage layer many database operators manage, see /containers/kubernetes-persistent-storage-guide/.
- For the templating alternative worth ruling out before building a custom operator, see /containers/helm-chart-development-guide/.
Frequently Asked Questions
What’s the difference between a Helm chart and an Operator? A Helm chart templates and deploys static configuration — it runs once at install or upgrade time. An Operator runs continuously, watching a Custom Resource and actively reconciling the cluster toward it in response to ongoing events, including ones a human didn’t trigger (a pod failure, a metric threshold).
Do I need an operator to run a stateful application on Kubernetes? Not automatically. A StatefulSet handles stable identity and ordered deployment natively. You need an operator specifically when day-two operations (failover, backup, scaling logic) require domain knowledge beyond what StatefulSets provide on their own.
Should I build a custom operator or use an existing one? Use an existing, well-maintained operator whenever the workload is a common system (Postgres, Kafka, Elasticsearch, and similar). Build a custom operator only for organization-specific operational logic with no existing equivalent.
What’s the biggest risk of building an operator? Ongoing maintenance burden and the risk that a bug in the operator’s reconciliation logic actively damages the state it’s supposed to protect — a materially worse failure mode than a misconfigured Helm chart, since the operator is making changes autonomously.



