Custom Resource Definitions
Topic 38

Custom Resource Definitions

ExtensibilityAPI

A Custom Resource Definition (CRD) teaches the API server a new kind of object. Once registered, your custom resource behaves like any built-in one — you kubectl apply it, get it, set RBAC on it, and watch it. CRDs are how Kubernetes becomes extensible without forking it.

On their own, CRDs just add new objects to store. The power comes when a controller watches them and acts — which is the operator pattern in the next topic. Understanding the CRD first makes operators make sense.

What a CRD Adds

A CRD registers a new API kind — say Database in group example.com/v1 — and from then on the API server accepts, validates, and stores objects of that kind. They get the full treatment: namespacing (or cluster scope), labels and selectors, RBAC, and the watch mechanism. To users and tools, a custom resource is indistinguishable from a native one; that uniformity is the whole point.

A CRD defining a new kind
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  group: example.com
  scope: Namespaced
  names:
    kind: Database
    plural: databases
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                engine: { type: string }
                sizeGi: { type: integer }

Schemas and Validation

A CRD carries an OpenAPI v3 schema, and you should always provide one. The schema validates incoming objects (rejecting a Database with a missing or mistyped field), documents the resource, and powers kubectl explain. Without a schema, anything is accepted and errors surface only later in the controller. The schema is the contract between whoever writes the custom resource and whatever reconciles it.

Versions and Subresources

CRDs support multiple versions with conversion between them, so you can evolve the API without breaking existing objects — important because once people depend on your kind, changes must be backward-compatible. CRDs can also enable subresources: a status subresource (so the controller updates status without touching spec, mirroring built-in objects) and a scale subresource (so an HPA can even scale your custom resource).

When a CRD Alone Is Not Enough

A CRD with no controller is just structured data in etcd — applying a Database object provisions nothing; it only records intent. That is occasionally useful (a CRD as a typed config store), but usually you want behavior, which means pairing the CRD with a controller (Topic 39). Also avoid using CRDs for high-churn, high-volume data: every custom object lives in etcd, and thousands of rapidly-changing ones pressure the control plane. CRDs are for declarative API objects, not as a general database.

CRD with vs without a controller

CRD only — adds a new object kind; storing one records intent but triggers no action. Useful as typed config at most.

CRD + controller — the object kind plus a reconcile loop that makes it real — the operator pattern (Topic 39).

Common Mistakes
  • Creating a CRD with no schema, so invalid objects are accepted and fail later.
  • Expecting a CRD alone to do something — without a controller it only stores data.
  • Making breaking changes to a CRD version that existing objects and tools depend on.
  • Using CRDs for high-volume, high-churn data and pressuring etcd and the API server.
  • Forgetting the status subresource, so the controller fights the user over the same object fields.
Best Practices
  • Always define an OpenAPI schema so custom resources are validated and self-documenting.
  • Version your CRD and provide conversion; treat the API as a contract once others depend on it.
  • Enable the status (and, where relevant, scale) subresource to mirror built-in behavior.
  • Pair a CRD with a controller when you want behavior, not just stored intent.
  • Keep CRDs for declarative API objects; don't use them as a high-churn datastore.
RelatedOperators and Controllers — add the reconcile loop that makes CRDs act (Topic 39)Extending the API — CRDs vs aggregated API servers (Topic 40)kubectl and the API — custom resources use the same surface (Topic 04)

Knowledge Check

What does a Custom Resource Definition do?

  • Registers a new API kind so custom objects behave like built-in ones (apply, get, RBAC, watch)
  • Deploys and starts the matching controller Deployment automatically as part of registration
  • Replaces the built-in API server with a dedicated process serving only that kind
  • Creates a dedicated namespace and binds the new resource exclusively inside it

You applied a custom resource but nothing was provisioned. Why?

  • A CRD alone only stores data; behavior requires a controller watching the resource
  • The CRD must be declared cluster-scoped before any object it defines will provision
  • Custom resources cannot carry a spec field, so nothing was written to act on
  • The API server silently drops custom resources instead of persisting them

Why should a CRD include an OpenAPI schema?

  • It validates objects, documents the resource, and powers kubectl explain
  • It deploys the resource's controller and wires up its reconcile loop on apply
  • It encrypts each stored object at rest in etcd using the schema's fields
  • A schema is mandatory before the resource can ever be namespaced

You got correct