CRD basics
A CRD is itself a Kubernetes object and uses the same top-level fields you already know:apiVersion, kind, metadata, and spec. For CRDs:
apiVersionis alwaysapiextensions.k8s.io/v1kindisCustomResourceDefinitionmetadata.namemust be set to the plural form of your resource followed by a dot and the API group (see note below)speccontainsgroup,scope,names, andversions
Always set
metadata.name to <plural>.<group>. For example, if your group is webapp.kodekloud.com and your plural is webapps, the metadata name must be webapps.webapp.kodekloud.com.spec fields
Versions and schema validation
A CRD can serve multiple versions. Important rules:- Exactly one version must have
storage: true— this is the version persisted to etcd. - Versions you want available via the API server should have
served: true. - Validation and defaults are expressed using
openAPIV3Schemafor each version.
v1alpha1 that is both served and storage. The following schema declares spec fields and constraints:
spec.image— string (required)spec.replicas— integer (required), minimum 1, maximum 10spec.port— integer, default 8080
/apis/webapp.kodekloud.com/v1alpha1
and kubectl discovery will expose the new resource types automatically.
Apply the CRD and confirm discovery
Apply the CRD manifest:NamesAccepted and Established):
kubectl explain immediately recognizes your new type:
Schema validation in action
Create a sample WebApp that violates thereplicas maximum (set to 99). Save this as sample-webapp.yaml:
replicas value (for example, to 3) and apply again:
spec (pipe through jq to format). Notice the server-populated default for port:
Next steps and useful extensions
You can extend this CRD to improve UX and integration with kubectl:- Add
statusas a subresource to allow controllers to update status safely. - Add a
scalesubresource to supportkubectl scale. - Define
additionalPrinterColumnssokubectl get webappdisplays meaningful columns.
Two common mistakes to watch for:
metadata.namemust beplural.group(example:webapps.webapp.kodekloud.com).- Exactly one version must have
storage: true— that is the version persisted in etcd.