Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To create a Kubernetes custom resource, define and apply a CustomResourceDefinition (CRD), then create an instance of the new type. The CRD registers the type and schema; it does not automate anything by itself. Add a controller only when the resource should trigger ongoing reconciliation or application-specific actions.

How do I create my first Kubernetes custom resource?

Start by deciding whether the object belongs in the Kubernetes API. A custom resource is most useful for declarative configuration or desired state that benefits from Kubernetes conventions, kubectl, API clients, watches, or automation. A CRD tells the API server what the new type looks like and lets it serve and store instances. Once registered, those instances can be managed with Kubernetes clients just like other API resources.

If you only need to provide an existing configuration file to a workload, a ConfigMap may be simpler. For imperative request/response operations, nonstandard REST paths, sustained high-volume traffic, or large end-user data, consider a standalone API instead. Custom resources use API-server storage and are a poor fit for large application data. Kubernetes documents these trade-offs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose what the object represents

Model a relatively small, declarative configuration object—not a general-purpose database or an action request. For example, an object might describe the desired configuration of an application component. If you need Kubernetes to react to that declaration, plan for a controller as well as the CRD.

What belongs in a CRD?

A CRD defines the API identity, scope, versions, and schema for a new resource type. Plan these elements before writing the manifest:

  • API group: Namespaces your API, such as example.com.
  • Plural and singular names: The plural is used as the resource name; the CRD’s full name is derived from that plural name and the group.
  • Kind: The type name users see in objects, such as Widget.
  • Scope: Choose whether instances are namespaced or cluster-scoped.
  • Version: Declare which API version clients can use and which version stores the data.
  • Schema: Specify the fields, types, and validation rules with the CRD’s OpenAPI v3 schema.

Keep the schema purposeful: define the fields users need and avoid a catch-all arbitrary object unless preserving arbitrary data is an explicit requirement. Kubernetes also supports status subresources and admission webhooks for CRDs. Consult the CRD task documentation for the manifest format and version-specific capabilities.

Choose scope deliberately

A namespaced custom object belongs to a namespace; deleting that namespace deletes its objects. A cluster-scoped object is not tied to a namespace. The CRD definition itself is non-namespaced, regardless of the scope selected for instances. Choose scope based on ownership, lifecycle, and access patterns—not convenience alone.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Apply the CRD, then create an instance

The essential sequence is to install the type before submitting objects that use it. The manifest below is a minimal illustrative pattern, not a complete production API; replace the example names and schema with your design, and check the CRD documentation for the Kubernetes release you target.

  1. Save a CRD manifest. Define the group, names, scope, served and storage version, and schema. For example, a namespaced type could use group example.com, kind Widget, and plural name widgets.
  2. Register the type: run kubectl apply -f widget-crd.yaml.
  3. Wait for establishment: run kubectl wait --for condition=Established --timeout=60s crd/widgets.example.com.
  4. Check API discovery: run kubectl api-resources --api-group=example.com. Confirm that the resource appears before creating an instance.
  5. Create an instance manifest: set apiVersion: example.com/v1, kind: Widget, a name under metadata, and values under spec that match your schema. For a namespaced type, provide a namespace or use the namespace selected by your kubectl context.
  6. Apply and inspect the instance: run kubectl apply -f widget.yaml, then kubectl get widgets and kubectl describe widget <name>. The API server can store and return the object even if no controller exists.

For exact syntax, including schema details and version declarations, follow the official CRD task guide. Commands and available capabilities can vary by Kubernetes version.

Do I need a controller for a CRD?

No—not just to register a type or store and retrieve its objects. As the Kubernetes documentation puts it, “On their own, custom resources let you store and retrieve structured data.” The custom resources guide distinguishes that basic API role from automation.

Add a controller when users expect the cluster to act on the declared state. A controller watches custom resources and repeatedly reconciles related Kubernetes objects or external effects so the observed system moves toward the desired state. A CRD paired with a controller is commonly associated with the operator pattern; an operator encodes application-specific operating knowledge in that controller-based extension.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CRD only or CRD plus controller?

Approach What it provides When it fits
CRD only A registered API type whose instances the API server serves and stores When clients need structured data in Kubernetes but no ongoing action is required
CRD plus controller The API type plus a process that watches instances and reconciles desired state When a declaration should create, update, or manage related resources or external effects

A controller is an additional operational component, not a feature switched on by adding fields to a CRD. Some packages install both a CRD and a controller, so account for the code and lifecycle of each.

Choosing a controller approach

If reconciliation is required, the Kubernetes operator guide lists frameworks and approaches including Kubebuilder, Operator Framework, Kopf, and Java Operator SDK. They are options, not a universal recommendation. Choose based on your language, project needs, and the maintenance model you can support. The Kubernetes operator guide explains the pattern.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Plan API versions and permissions

Served and storage versions

Decide which CRD version clients may use (served) and which version Kubernetes uses to store objects (storage). As the schema evolves, plan how existing objects and clients move between versions. If versions have schema differences that require custom conversion logic, Kubernetes supports conversion webhooks. See the CRD versioning guide before changing a version plan.

RBAC access

Custom resources use Kubernetes authentication, authorization, and audit logging, but existing roles do not automatically grant access to a newly introduced resource type. Add explicit RBAC rules for the custom resource and any subresources users or controllers need. The Kubernetes RBAC reference describes how permissions are granted.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common first-CRD mistakes

  • Expecting behavior from the CRD: It registers a type and schema; automation requires a controller.
  • Choosing scope casually: Namespace deletion removes namespaced instances, while cluster-scoped objects have different lifecycle and access semantics.
  • Leaving the schema vague: Define meaningful fields and validation so invalid desired state can be rejected.
  • Ignoring version evolution: Set served and storage versions intentionally, and plan conversion if schemas diverge.
  • Assuming permissions carry over: Grant RBAC access to the new type explicitly.
  • Using a custom resource as a data store: Keep it focused on configuration or desired state, not large application or end-user data.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.