
September 09, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Kubernetes Operators: Extend the API is how teams turn a generic cluster into a platform that understands your database, queue, or payment service. Native Kubernetes knows Pods, Deployments, and Services. It does not know how to fail over PostgreSQL or rotate TLS certificates for a multi-tenant SaaS. Operators close that gap by registering new object types and running controllers that enforce desired state continuously. If you already run workloads with Laravel on Kubernetes, operators are the next layer that keeps stateful services alive without midnight shell scripts.
What are Kubernetes Operators and how do they extend the API?
An operator is a controller plus one or more Custom Resource Definitions (CRDs). The CRD extends the Kubernetes API. Your team can then create objects like PostgresCluster or Certificate with kubectl, GitOps tools, or client libraries.
The controller watches those objects. It creates underlying Deployments, Services, Secrets, and PersistentVolumeClaims. It also runs domain logic that Helm charts and bash scripts usually leave to humans.
Core resources live in built-in API groups such as apps/v1. Custom resources live under your own group, for example database.example.com/v1. Once installed, they feel native. kubectl get postgresclusters works the same way as kubectl get deployments.
The official Kubernetes operator pattern documentation describes this as application-specific knowledge encoded in software. That is accurate. The practical win is repeatability. A junior engineer applies a YAML manifest. The operator performs steps that previously required a senior DBA.
On production clusters I help maintain, operators sit alongside GitOps controllers. Argo CD GitOps declares what should exist. Operators decide how to keep complex software healthy after it exists.
API aggregation vs CRDs
Most teams extend the API with CRDs. They are simpler to author and do not require an aggregated API server. API aggregation suits large platforms that expose many resources through a dedicated extension server. CRDs cover the majority of operator use cases in 2026.
How does a Custom Resource Definition work in Kubernetes?
A CRD is a manifest that teaches the API server about a new kind. You define group, version, scope, and an OpenAPI schema. After the CRD is established, users create instances of that kind.
Here is a minimal CRD for a fictional AppRelease resource:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: appreleases.platform.example.com
spec:
group: platform.example.com
scope: Namespaced
names:
plural: appreleases
singular: apprelease
kind: AppRelease
shortNames:
- ar
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [image, replicas]
properties:
image:
type: string
replicas:
type: integer
minimum: 1
status:
type: object
properties:
phase:
type: string
readyReplicas:
type: integer
subresources:
status: {}
additionalPrinterColumns:
- name: Image
type: string
jsonPath: .spec.image
- name: Ready
type: integer
jsonPath: .status.readyReplicas
Apply it, then create an instance:
kubectl apply -f apprelease-crd.yaml
apiVersion: platform.example.com/v1
kind: AppRelease
metadata:
name: billing-api
namespace: production
spec:
image: registry.example.com/billing:2.4.1
replicas: 3
Nothing happens yet except storage in etcd. The CRD alone extends the API surface. It does not extend behaviour. That requires a controller.
Status subresources and conditions
Well-designed operators write to .status, not .spec. Users own spec. The controller owns status. Use conditions such as Ready, BackupComplete, or FailoverInProgress. This mirrors built-in resources and plays nicely with GitOps. Argo CD should not fight the controller over status fields.
For schema validation during CI, pipe manifests through a JSON formatter and validator in your pipeline. Catch typos before they reach the cluster.
How does the operator reconciliation loop work?
Every controller follows the same loop. Watch events. Fetch the object. Compare desired spec with observed state. Act. Requeue if work remains.
Idempotency matters. The same event may arrive twice. Network partitions happen. The controller must tolerate partial failure and resume safely.
Finalizers prevent orphan resources. Add a finalizer on the custom resource. When a user deletes it, the controller runs cleanup—remove cloud load balancers, drop S3 buckets, revoke certificates—then removes the finalizer.
Leader election is required when you run multiple operator replicas. Without it, two controllers might fight over the same database failover. The Operator SDK client documentation covers manager options including leader election flags.
Owner references and garbage collection
Set ownerReferences on child objects. Kubernetes garbage-collects dependents when the parent custom resource disappears. This keeps the cluster tidy. I have seen hand-rolled scripts leave orphaned PVCs after app deletion. Operators should not repeat that mistake.
How do you build a Kubernetes Operator with Kubebuilder?
Kubebuilder and the Operator SDK are the standard Go scaffolds in 2026. They generate CRD manifests, RBAC, and controller stubs. You fill in reconcile logic.
- Install Go 1.22+ and the Kubebuilder CLI.
- Scaffold a project with
kubebuilder init --domain example.com --repo github.com/you/operator. - Create an API with
kubebuilder create api --group platform --version v1 --kind AppRelease. - Implement the
Reconcilemethod incontrollers/apprelease_controller.go. - Generate manifests with
make manifests install. - Deploy the manager Deployment to the target cluster.
A simplified reconcile skeleton:
func (r *AppReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var app platformv1.AppRelease
if err := r.Get(ctx, req.NamespacedName, &app); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
deploy := deploymentForApp(&app)
if err := controllerutil.SetControllerReference(&app, deploy, r.Scheme); err != nil {
return ctrl.Result{}, err
}
found := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
if err := r.Create(ctx, deploy); err != nil {
return ctrl.Result{}, err
}
} else if err != nil {
return ctrl.Result{}, err
} else if !reflect.DeepEqual(found.Spec, deploy.Spec) {
found.Spec = deploy.Spec
if err := r.Update(ctx, found); err != nil {
return ctrl.Result{}, err
}
}
app.Status.ReadyReplicas = found.Status.ReadyReplicas
app.Status.Phase = "Running"
if err := r.Status().Update(ctx, &app); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: time.Minute}, nil
}
Test with envtest or kind before production. Pair operator development with debugging CrashLoopBackOff pods skills. Your controller bugs often surface as failing manager pods first.
RBAC the operator needs
Grant least privilege. The controller needs read/write on its CRD, status subresource updates, and create/update/delete on child types it manages. Over-broad ClusterRole bindings are a common audit finding.
rules:
- apiGroups: ["platform.example.com"]
resources: ["appreleases", "appreleases/status"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
For teams shipping Laravel APIs on Kubernetes, operators can manage Redis, queue workers, and scheduled Jobs and CronJobs as a single custom resource. That reduces YAML sprawl across namespaces.
When should you use an Operator instead of Helm or plain manifests?
Helm installs templates. Operators maintain state over time. Choose based on operational complexity, not hype.
| Criteria | Plain Manifests / Kustomize | Helm | Operator (CRD + Controller) |
|---|---|---|---|
| Install once, rarely change | Good fit | Good fit | Overkill |
| Parameterized releases | Awkward | Strong fit | Possible but heavy |
| Day-2 ops: backup, failover, upgrade | Manual runbooks | Hooks help; still manual | Strong fit |
| Stateful clustered software | Risky | Risky at scale | Strong fit |
| Team skill requirement | Low | Low–medium | High (Go, K8s internals) |
| GitOps compatibility | Excellent | Excellent | Good with status separation |
Install Postgres once with Helm. Run a three-node HA cluster with automated backup and version upgrades? Use the Crunchy Postgres Operator, CloudNativePG, or similar. The same logic applies to cert-manager for TLS and the External Secrets Operator with Vault.
Crossplane is a related pattern. It extends the API to manage cloud infrastructure itself—RDS instances, S3 buckets, DNS records—not just in-cluster workloads. Platform teams often run Crossplane alongside application operators.
How do Operators fit into production clusters and GitOps workflows?
Production adoption starts with proven upstream operators. cert-manager, External Secrets, and Prometheus Operator are mature. Install them cluster-wide. Document version pins and upgrade windows.
GitOps tools sync custom resources like any other manifest. Store Certificate, ExternalSecret, or PostgresCluster YAML in Git. Argo CD applies it. The operator reconciles it. Keep spec in Git. Never commit status.
Watch for common production failures:
- Operator manager pod lacks RBAC for new API versions after upgrade.
- CRD schema changes break existing objects without a conversion webhook.
- Two controllers fight over the same labels on shared resources.
- Webhook timeouts block all creates when the operator is down.
- Resource limits on the manager cause OOMKill during bulk reconciles.
Set resource requests and limits on operator Deployments. They are control-plane-adjacent software. An OOMKill during certificate renewal is a bad day.
For edge or small clusters, K3s lightweight Kubernetes runs the same operator model. Resource budgets are tighter. Prefer single-replica operators with leader election disabled only when you accept the availability trade-off.
On a booking platform like Adventure Third Pole Trek, persistent storage and TLS are not optional. Operators reduce manual toil so the team focuses on application code rather than certificate expiry panics.
Platform work ties directly to Linux system administration and enterprise application development. Operators sit at the intersection. They are infrastructure code with application awareness.
Monitor operator health with Prometheus metrics. Track reconcile duration, error counts, and workqueue depth. Alert when errors spike after a cluster upgrade. Pair this with Horizontal Pod Autoscaling for the apps the operator manages—not always for the operator itself.
Upgrade strategy for CRD schema changes
Version CRD APIs carefully. Add a new version, mark the old one deprecated, run conversion webhooks, then remove the old version in a later release. Skipping conversion breaks existing objects silently. Test upgrades on a staging cluster that mirrors production CR counts.
Storage lifecycle matters too. Operators that manage databases depend on PersistentVolume lifecycle behaviour. Understand reclaim policies before you automate failover.
Key Takeaways
- Kubernetes Operators extend the API by registering CRDs and running controllers that reconcile spec with cluster state.
- CRDs alone add types; controllers add behaviour—both are required for a working operator.
- Prefer mature upstream operators (cert-manager, External Secrets, database operators) before writing custom Go controllers.
- Separate spec (GitOps-owned) from status (controller-owned) to avoid sync conflicts.
- Use finalizers, owner references, and leader election to prevent orphaned resources and split-brain failures.
- Build custom operators only when day-2 operational complexity justifies the engineering cost.
People Also Ask
What is the difference between a CRD and an Operator?
A CRD extends the Kubernetes API with a new object type. An operator is the running controller software plus that CRD. The CRD is the schema. The operator is the brain that watches instances and performs actions.
Do Kubernetes Operators replace Helm charts?
No. Helm excels at packaging and installing parameterized manifests. Operators excel at ongoing reconciliation—backups, upgrades, failover. Many teams use Helm to install the operator itself, then manage custom resources separately.
What language are Kubernetes Operators written in?
Most production operators use Go with Kubebuilder or Operator SDK. Go client libraries are first-class. Python and Java operators exist, but the ecosystem defaults to Go for performance and compiled single-binary deployment.
Can Operators manage resources outside the cluster?
Yes. Operators commonly call cloud APIs, DNS providers, and vault systems. Crossplane formalizes this for infrastructure. Application operators might rotate external API keys or provision managed database instances via cloud SDKs.
Ship platforms, not just manifests
Kubernetes Operators: Extend the API so your cluster understands operational work, not just container scheduling. Start with proven operators for TLS, secrets, and databases. Add custom CRDs only when your domain logic is unique and stable enough to encode.
If you are moving a Laravel or eCommerce workload onto Kubernetes and need help choosing operators, GitOps layout, or API integration patterns, contact us for a practical architecture review. You can also browse the portfolio for platforms already running on managed infrastructure, or read more on Ingress controllers and ongoing cluster maintenance.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

