Skip to main content
Kubernetes includes built-in types like Deployments, Services, and ConfigMaps for common workloads. Platforms often need higher-level, domain-specific abstractions — for example, a Database resource, TenantEnvironment, or MessageQueue — so users can request complex infrastructure without knowing implementation details. CustomResourceDefinitions (CRDs) extend the Kubernetes API with your own resource types. In this walkthrough we will:
  • Define a Database CRD with OpenAPI v3 schema validation.
  • Apply the CRD to a cluster.
  • Create example custom resources (CRs) to demonstrate validation errors and a final valid CR.
  • Inspect the created resource with kubectl.
This guide assumes basic familiarity with kubectl and a running Kubernetes cluster. For more details, see the Kubernetes CRD docs: CustomResourceDefinitions (CRDs).

CRD skeleton

Start with a minimal CRD skeleton. We’ll fill in details next.

Top-level fields explained

Use the table below as a quick reference for the main fields in the CRD manifest. Notes:
  • metadata.name must follow the convention <plural>.<group>; the API server enforces this.
  • Only one version in spec.versions should have storage: true. That version is persisted to etcd.
  • The OpenAPI v3 schema is validated by the API server when creating or updating custom resources.
OpenAPI v3 schema validation is enforced by the API server at object creation/update time. Only one version may have storage: true — that version is the one persisted to etcd.

Database CRD: concrete example

We’ll create a Database CRD in the platform.example.com group. It will be Namespaced and expose databases as the plural. The schema requires two fields in spec: engine and size, both validated with enum to limit allowed values.
Apply the CRD to your cluster:
Confirm the new resource appears in the API:
Your custom type now shows up alongside native Kubernetes resources and can be managed with kubectl.

Create Custom Resources (CRs) — iterate with examples

We’ll walk through several CR examples to illustrate typical validation errors and then the successful CR.
  1. Missing metadata.name
    If metadata.name is omitted, kubectl will fail before schema validation because resource names are required.
Applying it:
  1. Unsupported engine value
    Provide a name but use an engine value not allowed by the CRD schema (e.g., mongodb). The API server rejects this with a schema validation error.
Applying it:
  1. Missing required size field
    Change engine to a supported value but omit the required size field; the API server will report that size is required.
Applying it:
  1. Final valid CR
    Add the required size and any optional fields (like version and storage) to create a valid resource.
Apply the valid CR:
Verify the resource using the short name:
Describe the resource to inspect its fields:
(Backticks around <none> are used to avoid MDX interpreting angle brackets as JSX.)

Summary and next steps

This walkthrough showed how to:
  • Define a CRD with server-side OpenAPI v3 validation.
  • Apply the CRD so your custom type is available via the Kubernetes API.
  • Iterate on CRs and observe the API server enforcing schema constraints.
With a CRD in place, your custom resources become first-class objects in Kubernetes and can be managed with the same tools (kubectl, controllers, RBAC) as built-in resources. Next, consider:
  • Writing a controller/operator to reconcile Database resources into actual backing services.
  • Adding status subresources and additional validation with conversion or admission webhooks.
  • Documenting RBAC rules for users who will create Database CRs.
References

Watch Video

Practice Lab