Adetayo Akinsanya unkletayo.dev

Extending Kubernetes: Custom Resource Definitions (CRDs) and the Operator Pattern

Packaging human operational domain knowledge into automated Kubernetes controllers.

Adetayo Akinsanya (unkletayo) 2026-10-16

Part 18 in Series — Catch up on the previous article: Cluster Security & Authorization: RBAC, ServiceAccounts, and Admission Webhooks (Part 17) before diving into this post.

An enterprise data infrastructure team manages 50 production PostgreSQL database clusters.

Whenever a primary database node fails or requires a schema backup, a senior Database Administrator (DBA) follows a 15-step manual playbook:

  1. Promote secondary replica to primary.
  2. Update application connection strings.
  3. Trigger a point-in-time WAL backup to cloud object storage.
  4. Verify replication lag across surviving nodes.

Executing this playbook manually during outages takes 45 minutes, and human error risks data loss.

When the engineering team introduces a Custom Database Operator:

apiVersion: databases.example.com/v1alpha1
kind: PostgresCluster
metadata:
  name: production-db
spec:
  version: "15"
  replicas: 3
  storageSize: 500Gi
  backupSchedule: "0 2 * * *"

The human DBA’s operational domain knowledge is codified inside a Custom Controller.

When a node fails, the Custom Operator detects the failure, executes the failover playbook, updates internal endpoints, and triggers backups automatically in 4 seconds.

How do Custom Resource Definitions (CRDs) and The Operator Pattern extend Kubernetes beyond basic containers into intelligent, self-managing application platforms?


1. What Is a Custom Resource Definition (CRD)?

Out of the box, Kubernetes understands core native resources (Pods, Services, Deployments, ConfigMaps).

A Custom Resource Definition (CRD) is a built-in mechanism that allows developers to register brand new, custom API endpoints directly into the kube-apiserver schema catalog:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: postgresclusters.databases.example.com
spec:
  group: databases.example.com
  versions:
  - name: v1alpha1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              replicas:
                type: integer
              storageSize:
                type: string
  scope: Namespaced
  names:
    plural: postgresclusters
    singular: postgrescluster
    kind: PostgresCluster
    shortNames:
    - pg

What Happens When You Apply a CRD?

  1. The API Server registers new REST endpoints: /apis/databases.example.com/v1alpha1/namespaces/default/postgresclusters.
  2. kubectl immediately understands the new resource (kubectl get postgresclusters or kubectl get pg).
  3. Custom resource manifests applied to the cluster are validated against the defined OpenAPI v3 Schema and persisted natively in etcd.

2. What Is The Operator Pattern?

Registering a CRD stores desired state in etcd, but a CRD alone does nothing. Applying a CRD without a controller is just storing static data in a database.

The Operator Pattern combines a Custom Resource Definition (CRD) with a Custom Controller:

Operator=Custom Resource Definition (CRD)+Custom Reconciliation Controller\text{Operator} = \text{Custom Resource Definition (CRD)} + \text{Custom Reconciliation Controller}

+-------------------------------------------------------------------+
| 1. Custom Resource Manifest (e.g., kind: PostgresCluster)         |
|    Specifies desired database state                               |
+-------------------------------------------------------------------+
                                  |
                                  v  Stored in etcd via kube-apiserver
+-------------------------------------------------------------------+
| 2. Custom Operator Controller (Go / Java / Python)                |
|    Runs continuous reconciliation loop:                           |
|    - Observes PostgresCluster spec & actual DB state              |
|    - Manages underlying Deployments, StatefulSets, Services, PVCs |
|    - Executes operational domain tasks (Backups, Failovers)      |
+-------------------------------------------------------------------+

3. How an Operator Reconciles Stateful Applications

Consider what the PostgresCluster Operator does under the hood when a user applies replicas: 3:

User applies: `kind: PostgresCluster, spec: { replicas: 3 }`
                       |
                       v
[ Custom Operator Reconciliation Loop ]
  1. Reads PostgresCluster Spec.
  2. Checks if Headless Service exists -> If missing, creates Service!
  3. Checks if StatefulSet exists      -> If missing, creates StatefulSet!
  4. Checks if Backup CronJob exists   -> If missing, creates CronJob!
  5. Evaluates replication lag across database nodes.
  6. Writes current database health status into PostgresCluster `status` block.

Instead of requiring human engineers to manually coordinate StatefulSets, PVCs, and backup scripts, the Operator translates high-level domain intents into low-level Kubernetes primitives automatically.


The Operator Pattern powers the modern cloud-native ecosystem:

  • Prometheus Operator: Manages monitoring scrapers, alert rules, and Grafana dashboards.
  • Strimzi Kafka Operator: Automates Apache Kafka cluster provisioning, topic creation, and user ACL configuration.
  • Zalando Postgres Operator: Manages high-availability PostgreSQL clusters with automated WAL archiving.
  • Cert-Manager Operator: Automates ACME / Let’s Encrypt TLS certificate issuance and renewal.

Native Resources vs Custom Operators Comparison

FeatureNative Resources (Deployment, Service)Custom Operator (PostgresCluster)
API EndpointsStandard Kubernetes APIs (/api/v1)Custom API Extension (/apis/domain/v1)
Domain KnowledgeBasic container lifecycle & rolling updatesDeep domain-specific operations (DB backups, failovers)
Managed PrimitivesIndividual containersMulti-resource stacks (StatefulSet + PVC + Service + CronJob)
Implementation LanguageBuilt into kube-controller-manager (Go)External controller written in Go (Kubebuilder), Java, or Python

Summary & Next Steps

CRDs and Operators allow developers to extend Kubernetes into a custom cloud platform:

  • Custom Resource Definitions (CRDs) register custom API types and OpenAPI v3 schemas into kube-apiserver.
  • The Operator Pattern pairs a CRD with a Custom Controller running an automated reconciliation loop.
  • Operators codify human operational playbooks into self-healing software code.

In the next article, we examine Production Debugging and Operations: Event Loops, Pending Pods, and OOMKilled Analysis.

References & Further Reading

  1. Cloud Native Computing Foundation. Dynamic Admission Control (Validating and Mutating Webhooks). CNCF Docs.
  2. CNCF Kyverno Project. Kyverno Native Policy Management Architecture for Kubernetes. Kyverno Docs.
  3. CNCF OPA Project. Open Policy Agent (OPA) Gatekeeper Architecture. Gatekeeper Docs.

Up Next in Series →

Part 19: Production Debugging and Operations: Event Loops, Pending Pods, and OOMKilled Analysis

Continue to Part 19 →