
Problem: Mapping platform concepts to primitives
A common anti-pattern is modeling a single domain concept (e.g., a database) with multiple unrelated Kubernetes objects. For example, a single database might be represented by five separate resources: StatefulSet, ConfigMap, Secret, ServiceAccount, and NetworkPolicy.
- No domain-specific abstractions: you cannot represent a Database as a single, first-class object.
- No custom validation beyond primitive field checks (for example, you cannot require “production databases must have backups enabled” at the API layer).
- No simple trigger point for platform workflows when a logical resource changes.
- Platform concepts like teams, environments, and cost centers are not represented natively.

kubectl get databases and see meaningful results directly.
Kubernetes extension model — same API, new types
Kubernetes APIs follow a consistent URL pattern: group, version, namespace (if namespaced), and resource. For built-in Deployments the path is: /apis/apps/v1/namespaces/default/deployments Custom resources follow the same pattern. For example, a Database custom resource could be served at: /apis/platform.acme.io/v1/namespaces/dev/databases Because custom APIs use the same API server, they are first-class:kubectlcan get/list/describe/delete CRs.- RBAC can secure them.
- GitOps tools (ArgoCD, Flux) can sync them.
- Policy engines (Gatekeeper/Open Policy Agent) can enforce rules.
CRD anatomy
A CRD defines a new resource type. Key fields includegroup, names, scope, versions, and the openAPIV3Schema. Below is a concise reference table for the most important CRD fields.
Each version in
spec.versions can be served: true/false and one version can be storage: true (the persisted version).
Example CRD (illustrative)
This CRD demonstrates group, names, version, and anopenAPIV3Schema for validation.
metadata.namemust be<plural>.<group>(e.g.,databases.platform.acme.io).groupis typically your company or platform domain to avoid naming collisions.names.pluralis used for URLs andkubectl get.kindis the manifestkind(e.g.,Database).scopedetermines whether objects are cluster-scoped or namespaced.versionslet you evolve APIs;storage: truemarks which version is stored in etcd.openAPIV3Schemaenables API-server level validation (types, enums, required fields, numeric constraints, and defaults where supported).
Validation with OpenAPI v3 schemas
openAPIV3Schema is the first line of defense: the API server validates objects before passing them to controllers. The schema supports:
- Type validation:
string,integer,boolean,array,object. - Enums: restrict allowed values (e.g.,
size: enum [small, medium, large]). - Required fields: enforce mandatory inputs.
- Numeric constraints:
minimum,maximum. - Defaults: where supported, the API server can apply default values.
spec.size is restricted by an enum, sending xlarge will be rejected with a validation error. If replicas has maximum: 10, creating a resource with 100 replicas will be rejected.
CRD lifecycle and reconciliation loop
The lifecycle consists of three main players:- CRD — the class definition (schema and metadata) registered with the API server.
- CR (Custom Resource) — an instance that conforms to the CRD.
- Controller — code that watches CRs and reconciles desired state into real resources (StatefulSets, Secrets, external provisioning operations), and updates
status.

Example Custom Resource
An instance that conforms to the CRD shown above:Database resources and drives the provisioning workflow:
- Create Kubernetes objects (Secrets, StatefulSets, Services) or external resources (cloud DB instances).
- Update the CR
statuswith progress and conditions. - Reconcile changes (scaling, backups, configuration updates).
- Clean up resources when the CR is deleted.
Takeaways
- CRDs extend Kubernetes without modifying the control plane — they register new types with the API server.
- Custom resources use the same API URL patterns and kubectl tooling as built-in resources.
- OpenAPI v3 validation enforces correctness at the API layer before controller logic runs.
- The lifecycle is simple but powerful: CRD defines the schema, CR is the instance, and the controller implements the actions required to realize the instance.
CRDs make your platform concepts first-class Kubernetes resources so platform teams can expose domain-specific APIs that integrate seamlessly with kubectl, RBAC, GitOps, and policy engines — all without building a separate API server.
Further reading and references
- Kubernetes API concepts: https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/
- kubectl overview: https://kubernetes.io/docs/reference/kubectl/overview/
- RBAC docs: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- OpenAPI v3 specification: https://spec.openapis.org/oas/v3.0.3
- Gatekeeper / OPA: https://open-policy-agent.github.io/gatekeeper/