🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysMay 26, 20264 min read
Share:Tweet

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 describe on 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.

yaml
# 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.com

Kubernetes 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)

yaml
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:
    - app

After applying this, Kubernetes now accepts objects of kind Application.

Step 2: Create a Custom Resource (Use the Type)

yaml
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: default

Step 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

yaml
kind: Application    # Defines a GitOps sync target

The ArgoCD application-controller watches Application objects and syncs Git → cluster.

Cert-Manager — Certificate

yaml
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

yaml
kind: ServiceMonitor    # Tell Prometheus which services to scrape
kind: PrometheusRule    # Define alerting rules as Kubernetes objects

The Prometheus Operator watches ServiceMonitors and auto-configures Prometheus scrape configs.

Istio — VirtualService

yaml
kind: VirtualService    # Define traffic routing rules
kind: DestinationRule   # Define load balancing, circuit breaking

Crossplane — XRD (Composite Resource Definition)

yaml
kind: CompositeResourceDefinition    # Define your own cloud resources

Lets 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)

yaml
# 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

bash
# 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.io

CRD vs ConfigMap: When to Use Each

Use CaseCRDConfigMap
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:

yaml
# 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: WebApp

Now your developers can create apps without knowing Kubernetes internals:

yaml
apiVersion: platform.mycompany.com/v1
kind: WebApp
metadata:
  name: my-service
spec:
  image: myorg/myservice:v1.2.3
  replicas: 3
  port: 8080

A 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

TermDefinition
CRDSchema definition that extends the Kubernetes API
Custom ResourceAn instance of a CRD
ControllerCode that watches Custom Resources and acts
OperatorA 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

Browse fixes
Newsletter

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

Comments