LS LOGICIEL SOLUTIONS
Toggle navigation

What Is Custom Resource?

Definition

A Custom Resource is an extension of the Kubernetes API that lets a team define and use their own object type, alongside Kubernetes' built-in types like Pods and Services, without modifying Kubernetes itself. It's created by first registering a Custom Resource Definition, often shortened to CRD, which tells the Kubernetes API server the new type's name, its schema, and its validation rules, and once that definition exists, users can create actual custom resource objects of that type the same way they'd create any native Kubernetes object. On its own, a custom resource is just structured data stored in Kubernetes; it only drives real behavior when paired with a controller that watches it and acts. This extension mechanism has been part of Kubernetes for years now, and its stability is a large part of why the entire ecosystem of Operators, service meshes, and infrastructure tools was able to grow on top of it with confidence.

The reason custom resources exist is that Kubernetes' built-in object types cover generic workload and networking concerns well, but they have no way to represent the huge range of application-specific and infrastructure-specific concepts that different teams need to manage: a database cluster's configuration, a certificate's renewal policy, a message queue topic's partition count. Before CRDs existed, extending Kubernetes to understand a new concept meant either misusing an existing generic object type to awkwardly hold data it wasn't designed for, or forking and modifying Kubernetes' own source code, neither of which scaled to a broad ecosystem of different tools and needs. Both of those earlier approaches were genuinely painful in practice: stuffing structured application data into a generic ConfigMap gave up validation and discoverability, while maintaining a private Kubernetes fork meant carrying that maintenance burden forward through every future Kubernetes release.

What distinguishes a custom resource from just storing configuration in a database or a config file elsewhere is that once registered, it behaves exactly like a native Kubernetes object in every way that matters: it can be created, read, updated, and deleted through the standard Kubernetes API and command-line tools, it supports the same access control and audit logging as built-in types, and other tools in the Kubernetes ecosystem, dashboards, GitOps systems, policy engines, can interact with it without needing any special-case logic. That consistency is the entire point; a custom resource plugs into the same machinery everything else in the cluster already uses.

By 2026, custom resources have become the standard extension point for nearly every serious piece of Kubernetes-adjacent infrastructure, from Operators managing databases, to Crossplane's Compositions managing cloud infrastructure, to service meshes and certificate managers defining their own configuration objects. Nearly every mature Kubernetes-based tool ships its own CRDs as the primary way users configure and interact with it, which has made understanding what a CRD actually is and how to evaluate one a practical necessity for anyone operating a Kubernetes cluster of real size. It's genuinely rare at this point to find a serious cloud native tool that doesn't lean on CRDs somewhere in its design, which says as much about how well the extension model has held up as it does about any single tool's specific choices.

This page covers how Custom Resource Definitions and the resources they enable actually work, what makes a CRD well designed versus poorly designed, where custom resources fit inside the broader Kubernetes extension ecosystem, and how teams manage the practical realities of running clusters with many CRDs installed. The durable idea underneath the term is that Kubernetes was built to be extended rather than to be a fixed, closed system, and understanding custom resources gives a team the ability to evaluate any Kubernetes-based tool by asking what API it actually adds and how well that API is designed, rather than treating the tool as an opaque black box.

Key Takeaways

  • A Custom Resource Definition registers a new object type with the Kubernetes API server; a Custom Resource is an actual instance of that type.
  • On its own, a custom resource is just structured, validated data in Kubernetes; a controller has to watch it to make anything actually happen.
  • Custom resources behave exactly like native Kubernetes objects for access control, auditing, and tooling, which is what makes the extension model consistent.
  • Nearly every serious Kubernetes tool, from database Operators to Crossplane to certificate managers, uses CRDs as its primary configuration interface.
  • A cluster can accumulate many CRDs from different tools, and managing their versions, schemas, and access control becomes real, ongoing operational work.

CRDs and Custom Resources Are Two Different Things

It's worth separating these two terms clearly, because they get used almost interchangeably in casual conversation but describe genuinely different things. A Custom Resource Definition is the schema, the registration step where someone tells the Kubernetes API server "here is a new type called X, and here's what its fields look like, and here's how to validate them." Creating a CRD doesn't create any actual resources; it just makes a new type of object possible to create, the same way defining a class in a programming language doesn't create any instances of it. That analogy holds up well in conversation with engineers who already think in object-oriented terms, and it's often the fastest way to get someone new to Kubernetes to understand the distinction on the first try.

A Custom Resource is an actual object of that newly defined type, created the same way you'd create a Pod or a ConfigMap, by submitting a manifest to the Kubernetes API that matches the schema the CRD defined. If a `DatabaseCluster` CRD has been registered with fields for instance count and storage size, a user creates an actual `DatabaseCluster` custom resource by writing a YAML file with those specific values filled in and applying it to the cluster. From the API server's point of view, that manifest is handled through exactly the same request path as a built-in object, which is precisely what makes the whole extension mechanism feel native rather than bolted on.

This distinction matters practically because CRDs and custom resources have different lifecycles and different owners. A CRD is typically installed once, by a platform team or as part of installing some tool like an Operator or Crossplane, and it changes relatively rarely, usually only when the tool itself is upgraded to a new schema version. Custom resources, by contrast, get created and modified constantly by whoever is actually using that tool day to day, the same way Pods get created and destroyed far more often than the cluster's node types get redefined. That difference in change frequency is also why CRD changes typically go through much stricter review than day-to-day custom resource creation does, since a mistake in the schema itself affects every consumer at once.

Confusing the two leads to real operational mistakes, like a team assuming that deleting a custom resource object also removes the underlying CRD and the tool that depends on it, or the reverse, assuming that updating a CRD's schema automatically updates every existing custom resource object to match the new schema, which it does not do without explicit migration handling built into the responsible controller. The reverse mistake is just as common: deleting a CRD itself, rather than the individual custom resource objects, can cascade and delete every object of that type across the entire cluster at once, which is a genuinely dangerous action to take without understanding the blast radius first.

A Custom Resource Without a Controller Does Nothing

This is probably the single most important thing to understand about custom resources: creating one just stores structured data inside Kubernetes' API and its underlying storage. Nothing happens automatically as a result. If you define a `DatabaseCluster` CRD and create a `DatabaseCluster` custom resource with three replicas specified, no actual database pods get created unless something is specifically watching for `DatabaseCluster` objects and acting on them. This surprises people coming from other systems where defining a schema and populating a record with it are often assumed to trigger some kind of built-in behavior; in Kubernetes, that assumption simply doesn't hold.

That "something" is a controller, a piece of software, usually running as its own pod inside the cluster, that continuously watches the Kubernetes API for custom resources of a particular type and takes real action whenever one is created, updated, or found to be out of sync with the actual state of the world. This is exactly the pairing that makes up a Kubernetes Operator: the custom resource defines what's wanted, and the controller does the work of making it real and keeps checking that it stays that way. Crossplane's Compositions work the same way at a different layer, reconciling custom resources that describe cloud infrastructure rather than application components, which is a useful reminder that the custom resource pattern itself is generic and gets reused across very different kinds of controllers.

This separation between declaring desired state and acting on it is actually one of Kubernetes' most important design principles, not just an implementation detail specific to custom resources. It means the same custom resource can, in principle, be acted on by different controllers in different contexts, and it means a custom resource's data remains inspectable, auditable, and version-controllable through normal Kubernetes tooling regardless of what's actually doing the work behind the scenes. That auditability has real business value on its own: a security or compliance review can inspect exactly what infrastructure was requested and by whom just by reading the custom resource's history, without needing access to whatever external system actually carried out the request.

Teams new to Kubernetes sometimes assume that installing a CRD alone is sufficient to get some new capability, and then are confused when creating custom resource objects appears to do nothing. The troubleshooting step in that situation is always the same: check whether a controller for that custom resource type is actually running and healthy, because the custom resource itself, no matter how correctly formatted, is inert without one. It's worth building this check into a team's standard troubleshooting checklist rather than relying on people to remember it, since it's an easy step to skip when someone is already deep into debugging a more complicated-seeming problem.

Schema Design and Validation Shape How Safe a CRD Is

The schema defined inside a CRD, usually written using the OpenAPI validation format Kubernetes supports, determines what data is even allowed inside a custom resource of that type, and getting this schema right matters more than it might first appear. A well-designed CRD schema rejects invalid custom resource objects at creation time, before they ever reach a controller, catching a typo in a field name or an out-of-range value immediately with a clear error message instead of letting bad data through to fail mysteriously later.

A poorly designed CRD schema, one with loose validation or fields typed too permissively, lets invalid or nonsensical custom resource objects get created successfully, and the failure only shows up later, often deep inside a controller's reconciliation logic where the actual error message is far less clear about what went wrong. This is one of the clearest signals of a mature, well-built Kubernetes extension versus an early or hastily built one: how much validation work happens directly in the CRD schema versus being left to the controller to discover and report on its own. Debugging a controller-side failure caused by bad input is a genuinely worse experience than a schema-level rejection, since the error often surfaces as a confusing stack trace rather than a clear, immediate message pointing at the specific field that was wrong.

CRD schemas also support versioning, which lets a tool evolve its custom resource's shape over time, adding new fields or restructuring existing ones, while still supporting resources created under an older schema version. This versioning capability is exactly analogous to how Kubernetes itself has evolved its own built-in object types over years without breaking existing clusters, and tools that get this right can upgrade their CRDs without forcing every user to immediately rewrite every existing custom resource object they have in place. Conversion webhooks handle the translation between versions automatically in well-designed CRDs, which means an older custom resource object can still be read correctly by a controller written against the newer schema version without anyone manually migrating each one.

Evaluating a third-party tool's CRDs before adopting it is genuinely worth the time, specifically by looking at how thorough the schema validation is and whether the CRD has a clear versioning and upgrade story. A CRD with minimal validation and no apparent versioning plan is a signal that the tool behind it may be early-stage or not built with long-term production operation in mind, regardless of how compelling its feature list looks on first glance. This kind of due diligence takes maybe an hour of reading a project's CRD manifests directly, and it's a much better signal than reading the project's own marketing copy about how production-ready it claims to be.

Where Custom Resources Fit in the Kubernetes Extension Model

Custom resources are one part of a broader set of ways Kubernetes can be extended, and it's worth knowing where they sit relative to the others. Admission webhooks let a cluster intercept and validate or modify any object, not just custom ones, as it's being created. Aggregated API servers let a completely separate API server handle certain requests instead of Kubernetes' built-in one. Custom resources sit alongside these as the most common and generally the simplest extension mechanism, which is a large part of why they've become the default choice for most tools that need to add new concepts to Kubernetes. Many mature tools actually combine more than one of these mechanisms, using a CRD for the object model and an admission webhook to enforce additional validation rules the schema alone can't express.

This simplicity relative to the alternatives is a real strength. A CRD can be installed with a single manifest, requires no separate API server process to run and maintain, and immediately benefits from all of Kubernetes' existing machinery, storage, access control, auditing, watch and list operations, without any additional implementation work from the tool author. That's a large part of why the overwhelming majority of Kubernetes ecosystem tools, from Crossplane's Compositions to database Operators to service mesh configuration, chose custom resources as their extension mechanism instead of building a separate API server. Building and operating an aggregated API server, by contrast, is a meaningfully bigger undertaking, and it's reserved almost exclusively for cases with very specific performance or storage requirements that a CRD genuinely cannot meet.

Custom resources fit less well for extremely high-volume, high-churn data that changes far more frequently than typical Kubernetes objects, since the underlying storage, etcd, wasn't designed for extremely rapid, high-frequency writes at large scale. Tools that need to track something like per-request telemetry data generally use a purpose-built data store instead of trying to represent that data as custom resources, reserving CRDs for genuinely configuration-like, relatively low-churn data. A rough rule of thumb some teams use is to ask whether the data changes on the order of seconds to minutes or on the order of milliseconds; the former fits custom resources reasonably well, the latter almost always calls for a dedicated store instead.

It's also worth being clear that having a CRD doesn't automatically mean a tool is well integrated with the rest of the Kubernetes ecosystem. A tool's custom resources need to be designed thoughtfully to work well with GitOps tools, policy engines, and standard command-line tooling, and some tools' CRDs are structured in ways that make that integration awkward, for instance by embedding large blobs of unstructured data inside a single field instead of exposing meaningfully structured, individually addressable fields.

Managing CRDs Across a Real Kubernetes Cluster

A cluster of any real size accumulates CRDs from many different sources: Operators for databases, Crossplane and its providers, a certificate manager, a service mesh, monitoring tools, and more, and each of these is effectively adding new object types to the cluster's API surface that the platform team is implicitly responsible for understanding. Keeping track of what CRDs are installed, which tool owns each one, and what version of each is running becomes genuine operational work as the cluster and its tooling grow. It's not unusual for a mature production cluster to have well over a hundred CRDs installed once every Operator, service mesh component, and infrastructure tool has contributed its own, which makes an ad hoc, tribal-knowledge approach to tracking them increasingly unworkable.

CRD upgrades need particular care, because upgrading a tool that owns a CRD can, in some cases, change the CRD's schema in ways that affect existing custom resource objects already created under the old schema. A responsible upgrade process checks the tool's release notes for any breaking schema changes, tests the upgrade in a non-production environment against realistic existing custom resource objects, and has a rollback plan in case the new schema version doesn't handle existing data the way it's supposed to. Skipping that staging step is a common shortcut under deadline pressure, and it's exactly the kind of shortcut that turns a routine upgrade into an incident affecting every resource of that type across the cluster at once.

Access control around custom resources deserves the same attention platform teams give to native Kubernetes objects, and often gets less. Because CRDs use Kubernetes' standard role-based access control, it's straightforward to scope who can create, modify, or delete a specific custom resource type, but it's easy to overlook this during a tool's initial installation and end up with far broader access to sensitive custom resources, like ones representing cloud credentials or database configuration, than was actually intended. Reviewing the default roles a new tool ships with, rather than accepting them as-is, is a simple habit that catches most of these gaps before they become a real security exposure.

Finally, discoverability matters at scale. A cluster with dozens of CRDs installed benefits enormously from documentation, ideally centralized somewhere like an internal developer portal, that explains what each custom resource type is for, who owns it, and how to use it correctly. Without that, engineers encountering an unfamiliar custom resource type in a cluster have no efficient way to learn what it does short of reading the underlying tool's own external documentation, which slows down troubleshooting exactly when speed matters most.

Best Practices

  • Evaluate a CRD's schema validation and versioning story before adopting the tool behind it, not just its feature list.
  • Remember that a custom resource without a running, healthy controller does nothing; check controller health first when troubleshooting.
  • Scope role-based access control around sensitive custom resource types deliberately, rather than accepting broad default permissions.
  • Test CRD schema upgrades against realistic existing custom resource objects in a non-production environment before rolling them out.
  • Keep centralized documentation of what CRDs are installed in a cluster, who owns each one, and what they're for.

Common Misconceptions

  • Creating a Custom Resource Definition does not create any resources; it only registers a new type that resources of that type can then be created against.
  • A custom resource does not do anything by itself; it needs a controller actively watching it to have any real effect.
  • Updating a CRD's schema does not automatically update existing custom resource objects to match; that requires explicit migration handling.
  • Having a CRD does not automatically mean a tool is well designed or well integrated; schema quality and structure vary a lot between tools.
  • Custom resources are not meant for very high-frequency, high-volume data; that use case is better served by a purpose-built data store.

Frequently Asked Questions (FAQ's)

What is a Custom Resource Definition?

A Custom Resource Definition, or CRD, is a Kubernetes API extension that registers a new object type, including its schema and validation rules, with the Kubernetes API server, making it possible to create actual objects of that new type going forward.

What's the difference between a CRD and a custom resource?

A CRD is the schema or type registration itself; a custom resource is an actual instance of that type, created the way you'd create any native Kubernetes object like a Pod, once the CRD exists and has been applied to the cluster.

Does creating a custom resource actually do anything on its own?

No. A custom resource is just structured, stored data until a controller is running that watches for objects of that type and takes real action based on them, so a missing or unhealthy controller is always the first thing to check when nothing seems to happen.

Why would a tool use a Custom Resource Definition instead of a separate database?

Using a CRD means the tool's data automatically gets Kubernetes' existing access control, auditing, and command-line tooling for free, and integrates naturally with other Kubernetes-aware tools like GitOps systems, without extra implementation work.

Can a CRD's schema change over time?

Yes, CRDs support schema versioning, which lets a tool evolve its custom resource's structure over time while still supporting resources created under earlier schema versions, similar to how Kubernetes evolves its own built-in object types, often with conversion logic handling the translation between versions automatically.

What tools commonly use Custom Resource Definitions?

Kubernetes Operators for databases and messaging systems, Crossplane's Compositions for infrastructure, certificate managers, and service meshes are all common examples of tools that use CRDs as their primary configuration interface and interaction point with users.

How many CRDs can a Kubernetes cluster have installed?

There's no meaningfully low practical limit set by Kubernetes itself; a real production cluster commonly accumulates CRDs from many different installed tools, and managing them becomes genuine ongoing operational work, often reaching into the dozens or even hundreds once every Operator and infrastructure tool has contributed its own.

Is it risky to give broad access to custom resources?

Yes, particularly for custom resources that represent sensitive things like cloud credentials or database configuration. Access to specific custom resource types should be scoped deliberately using standard Kubernetes role-based access control, and default roles a tool ships with should be reviewed rather than accepted blindly.

How do I know if a tool's CRDs are well designed?

Look at how thorough its schema validation is, whether it has a clear versioning and upgrade story for schema changes, and whether its fields are structured and individually addressable rather than dumping unstructured data into large opaque blobs.