Chapter 46 of 47 · Under the hood

Operators: controllers with expert knowledge

Kubernetes knows how to keep 3 Pods running. It does not know how to fail over a PostgreSQL primary, take a backup, or upgrade Kafka without losing messages. An Operator teaches it: you add your own kind of object, like PostgresCluster, plus a controller that knows how to run that software the way an experienced engineer would.

The idea: a robot ops engineer

Imagine the person on your team who knows the database inside out: how to set it up, add a replica, back it up at night, switch to a replica when the primary dies. An operator is that knowledge written as code, running in the cluster, awake 24/7.

Operator = custom resource + controllerYou write a short custom resource. The operator's controller reads it and creates and manages the StatefulSet, Services, Secrets and backups the software needs. PostgresClusterinstances: 3you write 10 lines reads Operatora controller that knowsPostgreSQL StatefulSet + disks Services (rw, ro) Secrets (passwords) backups, failover
Operator = Custom Resource Definition + controller. The CRD adds a new word to the Kubernetes language. The controller (chapter 45) gives that word a meaning.

Step 1: a new kind of object (CRD)

A CustomResourceDefinition tells the API server: "from now on, accept objects of kind BlogSite, and here is what they may contain". Click the parts:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: blogsites.example.com
spec:
  group: example.com
  names:
    kind: BlogSite
    plural: blogsites
    shortNames: [blog]
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                version: { type: string }
                replicas: { type: integer, minimum: 1 }
                domain: { type: string }
      subresources:
        status: {}

names

The new kind and how kubectl calls it. After applying, kubectl get blogsites and kubectl get blog just work.

A CRD alone does nothing; it only teaches the API server to store your objects. Like a Deployment with no Deployment controller, it is just data.

Step 2: using your new kind

What you write

apiVersion: example.com/v1
kind: BlogSite
metadata:
  name: my-blog
spec:
  version: "1.4"
  replicas: 2
  domain: blog.example.com

What kubectl shows

$ kubectl apply -f my-blog.yaml
blogsite.example.com/my-blog created
$ kubectl get blog
NAME      VERSION   READY   AGE
my-blog   1.4       2/2     40s

It behaves like a built-in object: RBAC, labels, kubectl describe, GitOps tools all work with it. (The VERSION and READY columns come from additionalPrinterColumns in the CRD.)

Step 3: the controller that makes it real

The operator's controller watches BlogSites and, for each one, makes sure the right objects exist. For my-blog that might be the six Deployments and Services from chapter 17, an Ingress for the domain, and a ConfigMap. Change version: "1.5" and the operator rolls every service forward in the right order. Delete the BlogSite and owner references (chapter 45) clean everything up.

One short object in, a whole running app out. That is the payoff.

An operator at work

A database operator handling a bad night, step by step:

  1. PostgresCluster orders-db   instances: 3
      orders-db-1  primary   node-1
      orders-db-2  replica   node-2
      orders-db-3  replica   node-3

    Normal: one primary takes writes, two replicas copy it.

  2. node-1 ✗   orders-db-1 unreachable

    The primary's machine dies at 3 a.m.

  3. operator: primary lost
    operator: orders-db-2 is the most up-to-date replica → promote it
    operator: point Service orders-db-rw at orders-db-2

    Plain Kubernetes would only restart a Pod. The operator knows the database rule: promote the best replica, then move the "write" Service.

  4. operator: create a new replica orders-db-4, copy data from orders-db-2
    status: instances 3/3 ready, primary orders-db-2

    Back to 3 healthy members. Your app, which always connects to orders-db-rw, saw a few seconds of errors and nothing more. Nobody was woken up.

  1. spec:
      imageName: postgres:16.4   # was 16.3

    You change one line.

  2. operator: update replica orders-db-3 … ready
    operator: update replica orders-db-4 … ready

    Replicas first, one at a time.

  3. operator: switch primary to orders-db-3 (already upgraded)
    operator: update old primary orders-db-2 as a replica

    Then a planned, quick switchover, so the primary is never down for long. A plain StatefulSet rolling update knows nothing about primaries.

Operators you will meet

OperatorCustom resourcesDoes
CloudNativePGCluster, BackupPostgreSQL with failover, backups, upgrades
Prometheus OperatorPrometheus, ServiceMonitorRuns Prometheus and configures what it scrapes
cert-managerCertificate, ClusterIssuerGets and renews TLS certificates (chapter 27)
StrimziKafka, KafkaTopicRuns Apache Kafka clusters
$ kubectl get crds
NAME                                  CREATED AT
certificates.cert-manager.io          2026-03-02T10:14:00Z
clusters.postgresql.cnpg.io           2026-03-02T10:20:31Z
servicemonitors.monitoring.coreos.com 2026-03-02T10:21:05Z

Most are installed with one Helm chart, and many are listed on OperatorHub.io.

How smart is an operator?

Operators range from "installs the thing" to "runs it better than you". A common five-step scale:

LevelCan
1 · Basic installCreate the app from a custom resource
2 · UpgradesMove to a new version safely
3 · Full lifecycleBackups, restores, failover
4 · Deep insightsMetrics, alerts, useful status
5 · Auto pilotTunes and scales itself, fixes problems on its own

When choosing one for a database, check that it reaches at least level 3.

Building one

Kubebuilder and the Operator SDK (built on it) generate the CRD, the controller skeleton and the RBAC rules. You fill in one function: "reconcile this BlogSite". This is how most big operators are written.

When not to write one

Operators are powerful but are real software to maintain. Pick the simpler tool when you can:

Recap

Quick check

You apply a CRD for BlogSite and then a BlogSite object. kubectl get blog lists it, but no Pods appear. Why?
Show answer

The CRD only lets the API server store BlogSites. Nothing acts on them until the operator's controller is installed and running.

Operators: common questions

What is the difference between a CRD and an Operator?

A CRD only adds a new kind of object to the API. An Operator is a CRD plus a controller that acts on those objects to run and manage an application.