Skip to content
16 Jan, 2023

When NOT to create Kubernetes Operators

Industry Telecoms

While Kubernetes Operators have gained significant popularity in the DevOps community, they’re not always the right solution. This guide helps you determine when not to use the Operator pattern and what alternatives might better serve your needs.

 

Understanding the Kubernetes Operator Pattern


Over the past few years in the realm of DevOps and Kubernetes in particular, Kubernetes Operator pattern has been a trending topic.

In my personal experience, while working with different projects for different applications, it was evident that some of the software development teams, or organizations were too quick to jump on the Kubernetes Operator bandwagon, without analyzing the real-world problem they were trying to solve through the implementation of a Kubernetes Operator.

More often than not, the implementation of a kubernetes operator was done only because it was understood as the most trending topic or the implementation pattern in the kubernetes domain at that point.

I have seen certain software development teams going to the extent where they implement different wrapper CRDs, or controllers around an open source kubernetes operator, so that certain organizational practices and standards were encapsulated to these wrapper controllers while giving the organizations the ability to deploy and use the open-source Kubernetes Operator in their application stack.

If simply put, all that cost, time and effort from the software developers were invested in these kinds of implementations only to give the ability to use a specific, well known Kubernetes Operator in their application stack, while they had completely forgotten why anyone should really have a Kubernetes Operator or what actual problems a Kubernetes Operator is supposed to solve fundamentally in your application.

The purpose of this article is not to criticize the use of Kubernetes Operator pattern or manifest it as a bad practice.

It is in fact quite the opposite.

Kubernetes Operator pattern is a highly useful concept in a Kubernetes stack, which helps devops engineers or software developers to tackle certain complexities of the application lifecycle while deployed on a Kubernetes platform.

What we rather intend to illustrate through this article are the actual real world use cases that Kubernetes Operator pattern is supposed to solve, by first discussing the use cases which might not be applicable for it.

What Defines a Kubernetes Operator?

Let us first look at what is meant as an operator pattern in Kubernetes. There are three key points we should aim to suffice through an operator implementation.

  1. It should be a piece of software that automates a repeatable task of a stateful application and replaces the necessity of a human operator.
  2. It should be a software extension to the Kubernetes API that makes use of a Custom Resource.
  3. It should follow the Kubernetes principals, mainly the Control Loop.

While all three points above have an equal importance, we think the most important point out of three is the first one.

Operator vs Controller: Understanding the Distinction

Before diving deeper, it’s important to distinguish between Operators and Controllers:

  • Operators: Manage stateful applications with complex operational knowledge.
  • Controllers: Handle simpler, typically stateless workload management.

Key Difference:

Operators encode domain-specific operational knowledge.

 

Key Considerations Before Building an Operator


Now let us jump into our main topic, when should we not create an operator, or if we try to rephrase it in a more diplomatic way, “When should we rethink our decision to write an operator”.

1. Application State Analysis

Is my application a stateful application?

Even though official documentation for Kubernetes on operator pattern does not strictly mention about the application you are building the operator for, to be a stateful application, what we should understand is, an application which does not have a state will not really require some controller to handle its deployment lifecycle.

Notice how we have used the term “controller” here instead of “operator”. We will discuss this in a moment.

The simple reason being, if a particular application does not have a state, what it really means is you can pretty much use the native features of Kubernetes such as a deployment controller to handle the full life cycle of that application. 

But when an application is a stateful application then there is a possibility that you cannot freely replace a given replica of that particular application instance with a new replica (like it is supposed to happen in the kubernetes world all the time).

There is a chance some additional work such as leader elections, handling checkpoints, managing quorums, or restoring a backup is there that must be done when bringing up a new replica. Now, some of these tasks may not essentially be a part of the application code itself, maybe these are some manual steps that a human operator must execute. 

So, this is where the real use of an operator on kubernetes can pay off well. You can write a piece of software to encapsulate all that domain / application specific logic and then combine it to the Kubernetes API as an extension.

Where do the CRDs and Control Loop come into play in this context?

It is quite simple, CRDs pave the way for the end user to declare the desired state of the application that the operator has to maintain. End user will create / update a CR declaring the spec of the desired state, and then through the control loop, the operator will try to bring the application to that desired state. It will also report back the status of the application to the same CR.

Does this mean, we cannot write a similar operator to a stateless application? You absolutely can. 

However, in that case this should rather be considered as a controller not an operator.

Also, if you are thinking to write such a controller to manage the lifecycle of a stateless application, it would rather be an overkill because there should be other means to achieve the same thing just using the native Kubernetes API without having to extend it with a whole new set of CRDs.

Or this could even mean that your actual problem must be lying somewhere else, and you are misusing the operator pattern simply because you either want to use a Kubernetes object to handle a non-kubernetes resource in your application or you are trying to fix a configuration management problem through it.

Decision Matrix: Stateful vs Stateless Applications

2. Code Ownership and Maintenance

Any code is a liability

There are many frameworks available now to make the implementation of an operator an easy task. However, it still requires a certain effort from the developers to understand the complexity of the application and the actual business requirement you need to address through the operator’s control loop.

It is also seen now-a-days that certain teams, organizations develop operators for 3rd party open-source applications that are not owned by these teams or organizations.

The logic written into an operator is an imperative workflow that couples your application’s business logic into the Kubernetes control loop.

This may initially not look concerning to anyone, however the team that develops the operator will eventually be responsible to maintain the operator codebase to support the potential changes in the actual application that can impact its deployment lifecycle.

Even though writing an operator is not a difficult task now with the availability of different frameworks, it will still be something more complex than writing a piece of configuration to support deployment lifecycle with the use of native Kubernetes resources.

Also, unless you have a clear understanding of the potential changes coming into the particular application you are writing the operator for, you are putting yourself in a position where you have to invest a dedicated time and effort just to maintain the operator codebase to adhere to those changes from time to time, as they come.

This is the reason why it is recommended to leave the decision of writing an operator to the application owners or at least get enough understanding of the future changes the application may face, before starting writing an operator yourself for that application.

In either case, it is better to make yourself liable for a piece of configuration that you can easily modify than to make yourself liable for an entire codebase of an operator.

So, it is a wise decision to seek alternative ways that you can handle complexities in the lifecycle of an application rather than writing a piece of code that works as an operator and making yourself liable to it, especially if you do not own that application.

Maintenance Cost Analysis

Consider these ongoing costs before building an operator:

  • Development Time: 2-6 months initial development.
  • Maintenance Overhead: 20-30% of development time annually.
  • Testing Complexity: Unit tests, integration tests, e2e tests.
  • Documentation: User guides, API documentation, troubleshooting.
  • Support Burden: Issue triage, bug fixes, feature requests.

 

3. Resource Impact Assessment

Resource Concerns: Operators are not exactly part of your actual workload

In certain cases, you may have to run your application in a resource critical cluster. When you have an operator to manage the lifecycle of this application, the operator itself will require a certain level of resources (cpu, memory, network) allocated towards it from the same cluster where you run the workload.

What we expect from an operator is to maintain the lifecycle of a given number of application instances, by reconciling their state to a desired state, as specified by the user through the custom resource. 

Therefore, operators are a part of your control plane rather than the workload itself. Now imagine a situation where you must provision a large number of application instances.

This means a few things,

  1. You will have to create an equal number of custom resource objects.
  2.  The operator or operator instances will be running reconciliation loops to handle the state of all the application instances represented by each individual custom object. This could be resource intensive operation. 
  3. You are using the Kubernetes etcd store to keep track of all of the custom objects and your operator will be communicating with Kubernetes API quite frequently. 
  4. On all the other occasions where your custom objects are in the desired state, the operator will sit idle, but it still requires some resources from the cluster.

As you can see, having an operator to manage such a large number of custom objects could impact your cluster resource wise, in multiple ways.

Therefore, when you want to run your stateful application in a Kubernetes stack, writing an operator may sound appealing but you must remember the impact it may have on your cluster resources which is primarily meant for running your workload.

Resource Usage Patterns

Typical resource consumption for operators:

  • CPU: 50-200m (idle) to 500m+ (during reconciliation).
  • Memory: 64-256MB baseline, scaling with watched resources.
  • Network: Frequent API calls to Kubernetes control plane.

Storage: etcd usage for CRD storage and status updates.

Scale Considerations

4. Security Implications

Security Concerns: Is it worth running an operator with elevated privileges for the duration of your application?

This is one of the reasons why writing an operator should be your last resort. An operator is a highly privileged entity compared to your actual application.

If we take a step back and consider what your operator does, all it does is maintain the state of your application instances to a desired state as specified by the CR.

For doing so, it requires a certain level of privileges to your Kubernetes API. Now depending on the type of resource objects the operator is supposed to manage, you can grant permission to specific Kubernetes resources in either “namespace” scope or “cluster” scope.

In most cases, what we have seen in certain existing open-source operators is that they sometimes get deployed with RBAC permission to your cluster resources than they require.

Nevertheless, what is important here is that the operator will be running with all those permission to your Kubernetes cluster for the duration of your application, even when an actual reconciliation of the custom resources happens occasionally.

So, the amount of time the Kubernetes Operator will require these permissions to carry out its functionality is only a fraction compared to the time it will actually be running.

Considering these aspects of an operator, if you are thinking of writing one that will most probably do one-off tasks or tasks that will happen in a less frequent window (e.g., backup, restore), it could be worthwhile to analyze the possibility of using jobs or cron-jobs available in the standard Kubernetes API than investing all your effort to build a complex piece like a Kubernetes operator.

Common RBAC Permissions Required

Operators typically need permissions for:

  • Core Resources: pods, services, configmaps, secrets.
  • Extended Resources: deployments, statefulsets, persistent volumes.
  • Custom Resources: create, read, update, delete CRDs.
  • Cluster Resources: nodes, storage classes (for cluster-scoped operators).

Security Risk Mitigation

  • Principle of Least Privilege: Grant minimal required permissions.
  • Namespace Scoping: Prefer namespace-scoped over cluster-scoped.
  • Regular Audits: Review and update RBAC permissions.

Pod Security: Use security contexts and policies.

 

5. Configuration Management Anti-Pattern

Configuration Management: Operators should not be a solution to your configuration management problem

Something that we have seen in common in most operator implementations is that it helps end-users to manage the application configurations through a well-structured object like a custom resource. 

A custom resource has a predefined schema, managed through a custom resource definition (CRD). This CRD will be a dedicated one for the application, so validating the inputs a user can provide to configure the application state is more controlled and streamlined.

When you consider the standard ways that a user can pass application configurations, it is either using a configmap or a kubernetes secret which are more generic approaches.

A frequent implementation pattern that we have seen is that certain operators are sometimes implemented to just make use of this structured configuration that can be achieved using a CRD.

These operators mainly target to expose the application configurations via a CRD, so there is a control over the user inputs.

They should rather be called as controllers than operators because they do not essentially do anything specific to handle any application state related activities during the application lifecycle. While anyone is free to write a piece of software that is meant for handling configurations of an application through a CRD, it also may be an overkill

Because there is much more generic tooling available in the Kubernetes ecosystem, to achieve the same thing. 

For example, for someone using helm to manage a deployment and lifecycle of an application, certain features such as “–verify”, or json-schema validation are available to validate the user inputs which can eventually be mapped to a generic resource such as a configmap.

Question we should ask here really is, “is it really worth writing an application specific piece of software to manage the configuration, when there is much more generic tooling available to specifically address configuration related issues of applications deployed on Kubernetes?”

Configuration Management Alternatives

Instead of operators for configuration, consider:

  • Helm Charts: Template-based configuration with validation.
  • Kustomize: Declarative configuration management.
  • ArgoCD/Flux: GitOps-based configuration deployment.
  • External Secrets Operator: Generic secret management.

ConfigMap/Secret + ValidatingAdmissionWebhooks: Native validation

 

When Operators Are NOT the Right Solution


Based on the considerations above, avoid operators when:

1. Simple Stateless Applications

  • Use Instead: Deployments, Services, Ingress.
  • Example: Web applications, API services, microservices.

2. One-time or Infrequent Tasks

  • Use Instead: Jobs, CronJobs.
  • Example: Database migrations, backup scripts, batch processing.

3. Configuration-Only Management

  • Use Instead: Helm, Kustomize, ConfigMaps.
  • Example: Environment-specific configurations, feature flags.

4. Third-Party Applications (You Don’t Own)

  • Use Instead: Existing operators, Helm charts, managed services.
  • Example: Databases, monitoring tools, logging systems.

5. Resource-Constrained Environments

  • Use Instead: Serverless, managed services, simplified deployments.
  • Example: Edge computing, IoT, development clusters.

 

Alternative Solutions to Consider


Native Kubernetes Resources

  • StatefulSets: For stateful applications with simple requirements.
  • DaemonSets: For node-specific services.
  • Jobs/CronJobs: For batch processing and scheduled tasks.

Configuration Management Tools

  • Helm: Package management and templating.
  • Kustomize: Declarative configuration customization.
  • ArgoCD/Flux: GitOps deployment and management.

Managed Services

  • Cloud Provider Services: RDS, EKS, GKS managed databases.
  • SaaS Solutions: Monitoring, logging, security services.
  • Existing Operators: Well-maintained community operators.

 

Best Practices for Decision Making


The Operator Decision Tree, ask yourself these questions in order:

1 – Is my application stateful with complex operational requirements?

  • No → Use native K8s resources
  • Yes → Continue

2 – Do I own and understand the application lifecycle?

  • No → Look for existing solutions
  • Yes → Continue

3 – Will this operator be used long-term with ongoing maintenance?

  • No → Consider alternatives
  • Yes → Continue

4 – Do the benefits outweigh the resource and security costs?

  • No → Seek simpler solutions
  • Yes → Operator might be appropriate

 

Cost-Benefit Analysis Framework

Before building an operator, quantify:

Benefits:

  • Time saved on manual operations
  • Reduced human error
  • Standardized deployment procedures
  • Self-healing capabilities

Costs:

  • Development time (initial + ongoing)
  • Resource consumption
  • Security surface area
  • Maintenance overhead

 

Real World Examples


When NOT to Use Operators:

Example 1: Simple Web Application

  • Requirement: Deploy a stateless React application.
  • Wrong Approach: Custom operator for configuration management.
  • Right Approach: Deployment + Service + Ingress + ConfigMap.

Example 2: Database Backup

  • Requirement: Weekly database backups.
  • Wrong Approach: Complex operator with reconciliation loops.
  • Right Approach: CronJob with backup scripts.

Example 3: Third-Party Monitoring

  • Requirement: Deploy Prometheus for monitoring.
  • Wrong Approach: Custom wrapper operator.
  • Right Approach: Use existing Prometheus Operator or Helm chart.

When Operators ARE Appropriate

Example 1: Distributed Database

  • Requirement: Multi-node Cassandra cluster with automatic scaling.
  • Why Operator: Complex leader election, data rebalancing, backup/restore.

Example 2: Custom Stateful Application

  • Requirement: Proprietary workflow engine with state persistence.
  • Why Operator: Domain-specific operational knowledge, custom scaling logic.

 


These are the key areas that we would like to think a devops engineer or a software developer should consider, before starting to write a Kubernetes operator. 

We would like to end this article with the following note, “The fact that it is possible to write a Kubernetes operator as a solution to a given problem does not always mean you should write one.”

Key Takeaways

  1. Operators are for complex stateful applications – not configuration management.
  2. Consider maintenance costs – code is a liability.
  3. Evaluate resource impact – operators consume cluster resources.
  4. Assess security implications – operators require elevated privileges.
  5. Explore alternatives first – native K8s resources often suffice.

Making the Right Choice

The Kubernetes ecosystem offers many tools for different use cases. Operators are powerful but complex. Before building one, ensure you understand the problem you’re solving and have exhausted simpler alternatives.

 

Frequently Asked Questions


 

01 How do I know if my application is complex enough to warrant an operator?

 If your application requires domain-specific operational knowledge (like leader election, quorum management, or complex backup/restore procedures), an operator might be appropriate.

02 What's the difference between an operator and a controller?
03 Can I use existing operators instead of building my own?
04 What frameworks can help if I do need to build an operator?
05 How do I handle secrets and sensitive data in operators?

References:

  • https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
  • https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
  • https://thenewstack.io/kubernetes-when-to-use-and-when-to-avoid-the-operator-pattern/
  • https://sdk.operatorframework.io/docs/best-practices/best-practices/
  • Additional Resources: Operator Hub