What is a Kubernetes CRD? Custom Resources Explained Simply (2026)
CRDs extend the Kubernetes API with your own resource types. Learn what Custom Resource Definitions are, why they exist, and how tools like ArgoCD, Cert-Manager, and Prometheus use them.
Kubernetes comes with built-in resource types: Pods, Deployments, Services, ConfigMaps. But Kubernetes also lets you define your own resource types. These are called Custom Resource Definitions — CRDs.
Almost every tool you add to Kubernetes (ArgoCD, Cert-Manager, Prometheus Operator, Istio, Crossplane) works by defining CRDs and extending the Kubernetes API.
What is a CRD?
A CRD (Custom Resource Definition) is a way to teach Kubernetes about a new type of object.
Once you create a CRD, you can:
- Create objects of that type (called Custom Resources)
- Use
kubectl get,kubectl apply,kubectl describeon them - Write a controller that watches for changes and takes action
Real example: Cert-Manager adds a Certificate CRD. You apply a Certificate object, and Cert-Manager automatically requests and renews TLS certificates from Let's Encrypt.
# Without CRDs, you'd need scripts + manual kubectl commands
# With CRDs (cert-manager), you just apply a manifest:
apiVersion: cert-manager.io/v1
kind: Certificate ← custom resource type (not built into k8s)
metadata:
name: my-tls-cert
spec:
secretName: my-tls-secret
issuerRef:
name: letsencrypt-prod
dnsNames:
- myapp.example.comKubernetes doesn't know what a Certificate is by default. Cert-Manager installs a CRD that teaches Kubernetes about this type.
How CRDs Work
Step 1: Install a CRD (Define the Type)
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: applications.argoproj.io # <plural>.<group>
spec:
group: argoproj.io
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
scope: Namespaced
names:
plural: applications
singular: application
kind: Application
shortNames:
- appAfter applying this, Kubernetes now accepts objects of kind Application.
Step 2: Create a Custom Resource (Use the Type)
apiVersion: argoproj.io/v1alpha1
kind: Application ← our custom type
metadata:
name: my-app
namespace: argocd
spec:
source:
repoURL: https://github.com/myorg/myapp
path: k8s/
destination:
server: https://kubernetes.default.svc
namespace: defaultStep 3: A Controller Watches and Acts
The CRD defines the shape of the object. A controller (usually deployed as a Deployment) watches for changes to Custom Resources and takes action.
CRD = the schema (what fields are valid)
Custom Resource = an instance (the actual object you create)
Controller = the code that watches and acts on it
Real-World CRDs You Use Every Day
ArgoCD — Application
kind: Application # Defines a GitOps sync targetThe ArgoCD application-controller watches Application objects and syncs Git → cluster.
Cert-Manager — Certificate
kind: Certificate # Request a TLS certificate
kind: ClusterIssuer # Define how to get certificates (Let's Encrypt, self-signed)The cert-manager controller watches these and calls the ACME API.
Prometheus Operator — ServiceMonitor
kind: ServiceMonitor # Tell Prometheus which services to scrape
kind: PrometheusRule # Define alerting rules as Kubernetes objectsThe Prometheus Operator watches ServiceMonitors and auto-configures Prometheus scrape configs.
Istio — VirtualService
kind: VirtualService # Define traffic routing rules
kind: DestinationRule # Define load balancing, circuit breakingCrossplane — XRD (Composite Resource Definition)
kind: CompositeResourceDefinition # Define your own cloud resourcesLets platform teams define custom APIs like MySQLDatabase that provision RDS under the hood.
Why CRDs Matter
Before CRDs
Adding TLS automation meant:
- External script calling Let's Encrypt API
- Cron job checking expiry dates
- Manual secret creation in Kubernetes
- Shell scripts for renewal
After CRDs (Cert-Manager)
# This is it. Create this manifest. Done.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-cert
spec:
secretName: my-tls
issuerRef:
name: letsencrypt-prod
dnsNames: [myapp.example.com]CRDs let tool builders extend Kubernetes with domain-specific abstractions that feel native.
Working with CRDs
# List all CRDs in your cluster
kubectl get crds
# See all custom resources of a type
kubectl get applications -n argocd
kubectl get certificates -n default
kubectl get servicemonitors -n monitoring
# Describe a CRD to see its schema
kubectl describe crd applications.argoproj.io
# Delete a CRD (also deletes ALL resources of that type!)
kubectl delete crd applications.argoproj.ioCRD vs ConfigMap: When to Use Each
| Use Case | CRD | ConfigMap |
|---|---|---|
| Simple config key-values | ❌ | ✅ |
| Resource with lifecycle + status | ✅ | ❌ |
| Needs RBAC per object type | ✅ | ❌ |
Want kubectl get mytype | ✅ | ❌ |
| No controller needed | ❌ | ✅ |
Building a Simple CRD
If you want to create a platform abstraction for your team:
# Define a "WebApp" CRD for your platform
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: webapps.platform.mycompany.com
spec:
group: platform.mycompany.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [image, replicas]
properties:
image:
type: string
replicas:
type: integer
minimum: 1
port:
type: integer
default: 8080
scope: Namespaced
names:
plural: webapps
singular: webapp
kind: WebAppNow your developers can create apps without knowing Kubernetes internals:
apiVersion: platform.mycompany.com/v1
kind: WebApp
metadata:
name: my-service
spec:
image: myorg/myservice:v1.2.3
replicas: 3
port: 8080A controller (you'd write this in Go using controller-runtime, or use Crossplane/Operator SDK) watches for WebApp objects and creates the underlying Deployment + Service.
Summary
| Term | Definition |
|---|---|
| CRD | Schema definition that extends the Kubernetes API |
| Custom Resource | An instance of a CRD |
| Controller | Code that watches Custom Resources and acts |
| Operator | A controller that automates Day 2 operations |
CRDs are why Kubernetes is called the "platform for platforms" — it gives tool builders the primitives to extend it infinitely.
Related: Kubernetes Architecture Explained | How to Set Up Crossplane on Kubernetes | How to Set Up Backstage Developer Portal
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
What is a Kubernetes Operator? Explained Simply (2026)
Kubernetes Operators sound complex but they solve a simple problem: automating the management of stateful applications. Here's what they are and how they work.
Build an AI Kubernetes Runbook Generator with LLMs (2026)
Manual runbooks go stale. Build a system that watches your Kubernetes cluster, detects incidents, and generates step-by-step runbooks automatically using LLMs. Full implementation with Python, kubectl, and Ollama.
Build an AI Alert Classifier for Grafana Using LLMs (2026)
Tired of noisy Grafana alerts that wake you up for nothing? Build an AI layer that classifies incoming alerts as actionable or noise, enriches them with context, and routes them intelligently — using Claude or GPT-4 as the reasoning engine.