What a CRD actually is
A Custom Resource Definition (CRD) is a Kubernetes object that expands the Kubernetes API by registering a new resource type. Think of it like adding a new entry to a dictionary: before the CRD exists, the API server has no idea what a Database or a RedisCluster is. After you apply the CRD, the API server accepts, stores, and serves objects of that type just like it does for built-in types.
When you create a CRD, you are telling the Kubernetes API Server: "I want you to accept a new kind of YAML object called Database in the API group mycompany.io, version v1alpha1."
A CRD definition names the new type (its API group, version, plural/singular/short names) and, critically, an openAPIV3Schema — the schema validator. A Database CRD's schema might require an engineVersion and storageSize, and bound replicas between 1 and 5 with a default of 1. The API server uses this schema to reject malformed objects before they ever reach etcd. If a user submits a Database object without engineVersion, the API server returns a validation error immediately — no controller code needed.
Once this CRD is applied, users can interact with Database objects using the same standard commands they'd use for any built-in Kubernetes type.
Data without action
It is vital to understand that a CRD by itself does nothing but allow you to store a new kind of data in etcd.
If you create the Database CRD and then apply a Database object, Kubernetes will happily accept it and save it. But no database will actually spin up. No Pods will be created. No storage will be provisioned. Kubernetes knows what a Database object looks like, but it has no idea how to create one in the real world.
The CRD is the noun. You still need a verb — and that's where Operators come in.
Interviewers frequently ask: "What's the difference between a CRD and an Operator?" A common wrong answer is treating them as synonyms. The precise answer: a CRD is purely a schema registration — it teaches the API server a new type and enables storage and validation. An Operator is a CRD plus a controller that watches for instances of that type and takes real-world action. You can have a CRD without an Operator (useful for structured config storage), but an Operator always requires a CRD.