<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Operators Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/operators/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/operators/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Sat, 09 May 2026 18:40:28 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Operators Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/operators/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>CRDs You Already Use: cert-manager, KEDA, and External Secrets Explained</title>
		<link>https://linuxcent.com/kubernetes-custom-resources-examples/</link>
					<comments>https://linuxcent.com/kubernetes-custom-resources-examples/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 25 Apr 2026 18:15:04 +0000</pubDate>
				<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[cert-manager]]></category>
		<category><![CDATA[CRD]]></category>
		<category><![CDATA[Custom Resources]]></category>
		<category><![CDATA[External Secrets]]></category>
		<category><![CDATA[KEDA]]></category>
		<category><![CDATA[Operators]]></category>
		<guid isPermaLink="false">https://linuxcent.com/kubernetes-custom-resources-examples/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>See Kubernetes custom resources in action: cert-manager Certificates, KEDA ScaledObjects, and External Secrets — understand what CRDs look like before you build one.</p>
<p>The post <a href="https://linuxcent.com/kubernetes-custom-resources-examples/">CRDs You Already Use: cert-manager, KEDA, and External Secrets Explained</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>Kubernetes CRDs &amp; Operators: Extending the API, Episode 2</em><br />
<em><a href="/what-is-kubernetes-crd/">What Is a CRD?</a> · </em><em><a href="/kubernetes-custom-resources-examples/">CRDs You Already Use</a></em><em> · <a href="/kubernetes-crd-schema-explained/">CRD Anatomy</a> · <a href="/write-kubernetes-crd-yaml-walkthrough/">Write Your First CRD</a> · <a href="/kubernetes-crd-cel-validation/">CEL Validation</a> · <a href="/kubernetes-controller-reconcile-loop/">Controller Loop</a> · <a href="/build-kubernetes-operator-controller-runtime/">Build an Operator</a> · <a href="/kubernetes-crd-versioning-conversion-webhook/">CRD Versioning</a> · <a href="/kubernetes-admission-webhooks-explained/">Admission Webhooks</a> · <a href="/kubernetes-crd-production-finalizers-conditions-rbac/">CRDs in Production</a></em></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>cert-manager, KEDA, and External Secrets Operator are all CRD-based systems — understanding their custom resources shows you what a well-designed CRD looks like before you build one</li>
<li>cert-manager&#8217;s <code class="" data-line="">Certificate</code> CRD expresses desired TLS state; the cert-manager controller reconciles that state by issuing, renewing, and storing certificates in Secrets</li>
<li>KEDA&#8217;s <code class="" data-line="">ScaledObject</code> extends the HorizontalPodAutoscaler with external metrics (queue depth, Kafka lag, Prometheus queries) — the KEDA operator translates ScaledObjects into native HPA objects</li>
<li>External Secrets Operator&#8217;s <code class="" data-line="">ExternalSecret</code> abstracts over secret backends (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager) — the controller pulls values and writes Kubernetes Secrets</li>
<li>All three follow the same pattern: you describe <strong>desired state</strong> in a custom resource; the operator <strong>reconciles</strong> actual state to match</li>
<li>Kubernetes custom resources examples like these are the fastest way to internalize the CRD mental model before writing your own</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">  THREE CRD-BASED OPERATORS AND WHAT THEY MANAGE

  ┌─────────────────────────────────────────────────────────────┐
  │  cert-manager                                               │
  │  Certificate CR  →  controller issues cert  →  TLS Secret  │
  └─────────────────────────────────────────────────────────────┘

  ┌─────────────────────────────────────────────────────────────┐
  │  KEDA                                                       │
  │  ScaledObject CR  →  controller creates HPA  →  Pod count  │
  └─────────────────────────────────────────────────────────────┘

  ┌─────────────────────────────────────────────────────────────┐
  │  External Secrets Operator                                  │
  │  ExternalSecret CR  →  controller pulls  →  K8s Secret      │
  │                         from Vault/AWS/GCP                  │
  └─────────────────────────────────────────────────────────────┘

  In every case:
  User creates CR  →  Operator watches CR  →  Operator acts  →  Status updated
</code></pre>
<p>Kubernetes custom resources examples from real tools like these reveal the design pattern you will use in every CRD you build: express desired state declaratively, let the controller bridge the gap to actual state, surface the outcome in the status subresource.</p>
<hr />
<h2 id="why-look-at-existing-crds-first">Why Look at Existing CRDs First?</h2>
<p>Before designing your own CRD, you want to understand what good CRD design looks like from the user&#8217;s perspective. The engineers at Jetstack (cert-manager), KEDACORE (KEDA), and External Secrets contributors have collectively solved the same problems you will face:</p>
<ul>
<li>What goes in <code class="" data-line="">spec</code> vs <code class="" data-line="">status</code>?</li>
<li>How do you reference other Kubernetes objects?</li>
<li>How do you handle secrets and credentials securely?</li>
<li>What does a healthy vs unhealthy custom resource look like?</li>
</ul>
<p>Studying these before writing your own saves you from the most common first-timer mistakes.</p>
<hr />
<h2 id="cert-manager-the-certificate-crd">cert-manager: The Certificate CRD</h2>
<p>cert-manager is the most widely deployed CRD-based system in Kubernetes. It manages TLS certificates from Let&#8217;s Encrypt, internal CAs, and cloud providers.</p>
<h3 id="the-core-crds">The core CRDs</h3>
<pre><code class="" data-line="">kubectl get crds | grep cert-manager
</code></pre>
<pre><code class="" data-line="">certificates.cert-manager.io
certificaterequests.cert-manager.io
challenges.acme.cert-manager.io
clusterissuers.cert-manager.io
issuers.cert-manager.io
orders.acme.cert-manager.io
</code></pre>
<p>The one you interact with most is <code class="" data-line="">Certificate</code>. Here is a real example:</p>
<pre><code class="" data-line="">apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-tls
  namespace: production
spec:
  secretName: api-tls-cert        # cert-manager writes the TLS Secret here
  duration: 2160h                 # 90 days
  renewBefore: 720h               # renew 30 days before expiry
  subject:
    organizations:
      - example.com
  dnsNames:
    - api.example.com
    - api-internal.example.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
</code></pre>
<p>What happens after you apply this:</p>
<ol>
<li>cert-manager controller sees the new <code class="" data-line="">Certificate</code> object</li>
<li>It contacts the referenced <code class="" data-line="">ClusterIssuer</code> (Let&#8217;s Encrypt in this case)</li>
<li>It completes the ACME challenge, obtains the certificate</li>
<li>It writes the certificate and private key into the <code class="" data-line="">api-tls-cert</code> Secret</li>
<li>It updates the <code class="" data-line="">Certificate</code> object&#8217;s <code class="" data-line="">status</code> to reflect success</li>
</ol>
<pre><code class="" data-line="">kubectl describe certificate api-tls -n production
</code></pre>
<pre><code class="" data-line="">Status:
  Conditions:
    Last Transition Time:  2026-04-10T08:00:00Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2026-07-09T08:00:00Z
  Not Before:              2026-04-10T08:00:00Z
  Renewal Time:            2026-06-09T08:00:00Z
</code></pre>
<h3 id="what-this-teaches-you-about-crd-design">What this teaches you about CRD design</h3>
<ul>
<li><code class="" data-line="">spec.secretName</code> — the CR references an output object by name. The controller creates or updates that object.</li>
<li><code class="" data-line="">spec.issuerRef</code> — the CR references another custom resource (<code class="" data-line="">ClusterIssuer</code>) by name. This is a common pattern for separating configuration concerns.</li>
<li><code class="" data-line="">status.conditions</code> — the standard Kubernetes condition pattern: <code class="" data-line="">type</code>, <code class="" data-line="">status</code>, <code class="" data-line="">reason</code>, <code class="" data-line="">message</code>. You will use the same structure in your own CRDs.</li>
<li>The controller owns <code class="" data-line="">status</code> — users own <code class="" data-line="">spec</code>. This separation is a core convention.</li>
</ul>
<hr />
<h2 id="keda-the-scaledobject-crd">KEDA: The ScaledObject CRD</h2>
<p>KEDA (Kubernetes Event-Driven Autoscaling) extends Kubernetes autoscaling beyond CPU and memory. It can scale deployments based on queue depth, Kafka consumer lag, Prometheus metric values, and dozens of other event sources.</p>
<h3 id="the-core-crds_1">The core CRDs</h3>
<pre><code class="" data-line="">kubectl get crds | grep keda
</code></pre>
<pre><code class="" data-line="">clustertriggerauthentications.keda.sh
scaledjobs.keda.sh
scaledobjects.keda.sh
triggerauthentications.keda.sh
</code></pre>
<p>A <code class="" data-line="">ScaledObject</code> ties a Deployment to an external scaler:</p>
<pre><code class="" data-line="">apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaler
  namespace: production
spec:
  scaleTargetRef:
    name: order-processor        # the Deployment to scale
  minReplicaCount: 0             # scale to zero when idle
  maxReplicaCount: 50
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.us-east-1.amazonaws.com/123456789/orders
        queueLength: &quot;5&quot;         # target: 5 messages per pod
        awsRegion: us-east-1
      authenticationRef:
        name: keda-sqs-auth      # TriggerAuthentication for AWS credentials
</code></pre>
<p>What KEDA does with this:</p>
<ol>
<li>KEDA controller sees the <code class="" data-line="">ScaledObject</code></li>
<li>It creates a native <code class="" data-line="">HorizontalPodAutoscaler</code> object targeting the <code class="" data-line="">order-processor</code> Deployment</li>
<li>KEDA&#8217;s metrics adapter polls the SQS queue depth and exposes it as a custom metric</li>
<li>The HPA uses that metric to scale replicas — including to zero when the queue is empty</li>
</ol>
<pre><code class="" data-line="">kubectl get scaledobject order-processor-scaler -n production
</code></pre>
<pre><code class="" data-line="">NAME                       SCALETARGETKIND      SCALETARGETNAME    MIN   MAX   TRIGGERS         READY   ACTIVE
order-processor-scaler     apps/Deployment      order-processor    0     50    aws-sqs-queue    True    True
</code></pre>
<h3 id="what-this-teaches-you-about-crd-design_1">What this teaches you about CRD design</h3>
<ul>
<li><code class="" data-line="">spec.scaleTargetRef</code> — targeting another object by name. The controller acts on that object, not on the CR itself.</li>
<li><code class="" data-line="">spec.triggers</code> — a list of trigger specifications. Lists of typed sub-objects are a recurring CRD pattern.</li>
<li><code class="" data-line="">spec.minReplicaCount: 0</code> — expressing scale-to-zero as a first-class concept in the API. Built-in HPA does not support this; KEDA&#8217;s CRD extends the vocabulary of what is expressible.</li>
<li>The KEDA operator translates <code class="" data-line="">ScaledObject</code> → native HPA. The CRD is an abstraction over a more complex Kubernetes object. This &#8220;translate and manage child resources&#8221; pattern is extremely common in operators.</li>
</ul>
<hr />
<h2 id="external-secrets-operator-the-externalsecret-crd">External Secrets Operator: The ExternalSecret CRD</h2>
<p>External Secrets Operator (ESO) solves a specific problem: secrets live in external systems (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager), but Kubernetes workloads need them as Kubernetes Secrets. ESO bridges the gap.</p>
<h3 id="the-core-crds_2">The core CRDs</h3>
<pre><code class="" data-line="">kubectl get crds | grep external-secrets
</code></pre>
<pre><code class="" data-line="">clusterexternalsecrets.external-secrets.io
clustersecretstores.external-secrets.io
externalsecrets.external-secrets.io
secretstores.external-secrets.io
</code></pre>
<p>A <code class="" data-line="">SecretStore</code> defines the backend connection:</p>
<pre><code class="" data-line="">apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-manager
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-sa            # uses IRSA/workload identity
</code></pre>
<p>An <code class="" data-line="">ExternalSecret</code> defines what to pull and how to map it:</p>
<pre><code class="" data-line="">apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-creds
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: database-secret          # Kubernetes Secret to create/update
    creationPolicy: Owner
  data:
    - secretKey: username          # key in the K8s Secret
      remoteRef:
        key: prod/database         # path in AWS Secrets Manager
        property: username         # property within that secret
    - secretKey: password
      remoteRef:
        key: prod/database
        property: password
</code></pre>
<p>After ESO reconciles this:</p>
<pre><code class="" data-line="">kubectl get secret database-secret -n production -o jsonpath=&#039;{.data.username}&#039; | base64 -d
# outputs: db_user
</code></pre>
<pre><code class="" data-line="">kubectl describe externalsecret database-creds -n production
</code></pre>
<pre><code class="" data-line="">Status:
  Conditions:
    Last Transition Time:   2026-04-10T08:00:00Z
    Message:                Secret was synced
    Reason:                 SecretSynced
    Status:                 True
    Type:                   Ready
  Refresh Time:             2026-04-10T09:00:00Z
  Synced Resource Version:  1-abc123
</code></pre>
<h3 id="what-this-teaches-you-about-crd-design_2">What this teaches you about CRD design</h3>
<ul>
<li><code class="" data-line="">spec.secretStoreRef</code> — referencing a configuration CRD (<code class="" data-line="">SecretStore</code>) from an operational CRD (<code class="" data-line="">ExternalSecret</code>). This layering of CRDs to separate concerns is a mature pattern.</li>
<li><code class="" data-line="">spec.refreshInterval</code> — the CR expresses a desired behavior (periodic sync), not just a desired state snapshot. CRDs can express temporal behaviors.</li>
<li><code class="" data-line="">spec.target.creationPolicy: Owner</code> — ESO will set an owner reference on the created Secret, so deleting the <code class="" data-line="">ExternalSecret</code> cascades to deleting the Secret. This is how controllers manage lifecycle.</li>
<li>Sensitive values never appear in the CR — only paths and references. The controller handles the actual secret retrieval. This is a key security pattern in CRD design.</li>
</ul>
<hr />
<h2 id="the-common-pattern-across-all-three">The Common Pattern Across All Three</h2>
<pre><code class="" data-line="">  OPERATOR PATTERN (cert-manager / KEDA / ESO / every other operator)

  User applies CR
        │
        ▼
  Controller watches CRDs
  (informer cache, events queue)
        │
        ▼
  Controller reconciles:
  actual state ──→ compare ──→ desired state
        │              │
        │         (gap found)
        │              │
        ▼              ▼
  Takes action      Updates status
  (issue cert,      conditions in CR
   create HPA,
   sync Secret)
        │
        └──── loops back, watches for next change
</code></pre>
<p>The design contract:<br />
&#8211; <strong>Users write <code class="" data-line="">spec</code></strong> — what they want<br />
&#8211; <strong>Controllers read <code class="" data-line="">spec</code>, write <code class="" data-line="">status</code></strong> — what actually happened<br />
&#8211; <strong>Status conditions are truth</strong> — <code class="" data-line="">Ready: True/False</code> with <code class="" data-line="">reason</code> and <code class="" data-line="">message</code> tell operators what the controller knows</p>
<p>This pattern, <a href="/kubernetes-controller-reconcile-loop/">explained in depth in EP06</a>, is why CRDs and controllers are designed the way they are.</p>
<hr />
<h2 id="common-mistakes"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Mistakes</h2>
<p><strong>Installing CRDs without the controller.</strong> If you install cert-manager&#8217;s CRDs from the <code class="" data-line="">crds.yaml</code> manifest without installing cert-manager itself, <code class="" data-line="">Certificate</code> objects will be accepted by the API server but never reconciled. The <code class="" data-line="">Ready</code> condition will never appear. Always install the operator alongside its CRDs.</p>
<p><strong>Editing status fields directly.</strong> Many teams try <code class="" data-line="">kubectl patch</code> or <code class="" data-line="">kubectl edit</code> to update a custom resource&#8217;s status to work around a stuck controller. Most well-written controllers overwrite status every reconcile loop — your manual change will be wiped. Fix the underlying issue, not the status display.</p>
<p><strong>Assuming CRD deletion is safe.</strong> Covered in EP01 but worth repeating: deleting a CRD cascades to deleting all instances. If you <code class="" data-line="">kubectl delete crd certificates.cert-manager.io</code>, every <code class="" data-line="">Certificate</code> object in every namespace is gone and cert-manager will stop issuing. Back up CRDs and their instances before any CRD deletion.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<pre><code class="" data-line=""># See all CRDs installed by cert-manager
kubectl get crds | grep cert-manager.io

# Get all Certificates across all namespaces
kubectl get certificates -A

# Watch cert-manager reconcile a new Certificate
kubectl get certificate api-tls -n production -w

# See all ScaledObjects and their current state
kubectl get scaledobjects -A

# Check ESO sync status for all ExternalSecrets
kubectl get externalsecrets -A

# Inspect what APIs a CRD exposes
kubectl api-resources | grep cert-manager
</code></pre>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>cert-manager, KEDA, and ESO are canonical examples of well-designed CRD-based operators</li>
<li>All three follow the same pattern: user writes <code class="" data-line="">spec</code>, controller reconciles to actual state, status reflects outcome</li>
<li><code class="" data-line="">spec</code> expresses desired state declaratively; the controller figures out how to achieve it</li>
<li>Status conditions (<code class="" data-line="">type</code>, <code class="" data-line="">status</code>, <code class="" data-line="">reason</code>, <code class="" data-line="">message</code>) are the standard way to surface controller outcomes</li>
<li>Sensitive values never appear in the CR — controllers retrieve them from external systems using references and credentials</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p><a href="/kubernetes-crd-schema-explained/">EP03: CRD Anatomy</a> opens the YAML of a CRD itself — <code class="" data-line="">spec.versions</code>, OpenAPI schema properties, scope, names, and subresources. You have seen CRDs from the outside; next we look at how they are structured on the inside.</p>
<p>Get EP03 in your inbox when it publishes → <a href="https://linuxcent.com">subscribe at linuxcent.com</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&amp;linkname=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fkubernetes-custom-resources-examples%2F&#038;title=CRDs%20You%20Already%20Use%3A%20cert-manager%2C%20KEDA%2C%20and%20External%20Secrets%20Explained" data-a2a-url="https://linuxcent.com/kubernetes-custom-resources-examples/" data-a2a-title="CRDs You Already Use: cert-manager, KEDA, and External Secrets Explained"></a></p><p>The post <a href="https://linuxcent.com/kubernetes-custom-resources-examples/">CRDs You Already Use: cert-manager, KEDA, and External Secrets Explained</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/kubernetes-custom-resources-examples/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1678</post-id>	</item>
		<item>
		<title>What Is a Kubernetes CRD? How Custom Resources Extend the API</title>
		<link>https://linuxcent.com/what-is-kubernetes-crd/</link>
					<comments>https://linuxcent.com/what-is-kubernetes-crd/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 25 Apr 2026 18:14:48 +0000</pubDate>
				<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[API Extension]]></category>
		<category><![CDATA[CRD]]></category>
		<category><![CDATA[Custom Resource Definition]]></category>
		<category><![CDATA[kubectl]]></category>
		<category><![CDATA[Operators]]></category>
		<category><![CDATA[Platform Engineering]]></category>
		<guid isPermaLink="false">https://linuxcent.com/what-is-kubernetes-crd/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Learn what a Kubernetes CRD is, how custom resources extend the API server, and why every operator depends on them — with real kubectl examples for platform engineers.</p>
<p>The post <a href="https://linuxcent.com/what-is-kubernetes-crd/">What Is a Kubernetes CRD? How Custom Resources Extend the API</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>Kubernetes CRDs &amp; Operators: Extending the API, Episode 1</em><br />
<em><a href="/what-is-kubernetes-crd/">What Is a CRD?</a> · <a href="/kubernetes-custom-resources-examples/">CRDs You Already Use</a> · <a href="/kubernetes-crd-schema-explained/">CRD Anatomy</a> · <a href="/write-kubernetes-crd-yaml-walkthrough/">Write Your First CRD</a> · <a href="/kubernetes-crd-cel-validation/">CEL Validation</a> · <a href="/kubernetes-controller-reconcile-loop/">Controller Loop</a> · <a href="/build-kubernetes-operator-controller-runtime/">Build an Operator</a> · <a href="/kubernetes-crd-versioning-conversion-webhook/">CRD Versioning</a> · <a href="/kubernetes-admission-webhooks-explained/">Admission Webhooks</a> · <a href="/kubernetes-crd-production-finalizers-conditions-rbac/">CRDs in Production</a></em></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>A Kubernetes CRD (Custom Resource Definition) is how you add new resource types to the Kubernetes API — the same way <code class="" data-line="">Deployment</code> and <code class="" data-line="">Service</code> exist natively, you can make <code class="" data-line="">BackupPolicy</code> or <code class="" data-line="">Certificate</code> exist too<br />
  <em>(CRD = the schema/blueprint; Custom Resource = an instance of that schema, just like a Pod is an instance of the Pod schema)</em></li>
<li>Every <code class="" data-line="">kubectl get crds</code> on a real cluster shows dozens of them — cert-manager, KEDA, Prometheus Operator, Crossplane all ship their own CRDs</li>
<li>CRDs are served by the same API server as built-in resources — <code class="" data-line="">kubectl</code>, RBAC, watches, and events all work identically</li>
<li>A CRD alone does nothing — a <strong>controller</strong> watches the custom resources and acts on them; together they form an <strong>Operator</strong></li>
<li>CRDs live in etcd just like Pods and Deployments — they survive API server restarts and cluster upgrades</li>
<li>You do not need to modify Kubernetes source code or restart the API server to add a CRD</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">  HOW KUBERNETES CRDs EXTEND THE API

  ┌──────────────────────────────────────────────────────────────┐
  │  Kubernetes API Server                                       │
  │                                                              │
  │  Built-in resources          Custom resources (via CRD)      │
  │  ─────────────────           ──────────────────────────      │
  │  Pod                         Certificate     (cert-manager)  │
  │  Deployment                  ScaledObject    (KEDA)          │
  │  Service                     ExternalSecret  (ESO)           │
  │  ConfigMap                   BackupPolicy    (your team)     │
  │  ...                         ...                             │
  │                                                              │
  │  All resources: same API, same kubectl, same RBAC, same etcd │
  └──────────────────────────────────────────────────────────────┘
            ▲                          ▲
            │ built in                 │ registered at runtime
            │                         │
         Kubernetes              CustomResourceDefinition
          binary                    (a YAML you apply)
</code></pre>
<p>What is a Kubernetes CRD? It is a resource that defines resources — a schema registration that teaches the API server about a new object type you want to use in your cluster.</p>
<hr />
<h2 id="what-problem-crds-solve">What Problem CRDs Solve</h2>
<p>Kubernetes ships with roughly 50 resource types: Pods, Deployments, Services, ConfigMaps, Secrets, PersistentVolumes, and so on. These cover the general-purpose building blocks for running containerized workloads.</p>
<p>But the moment you operate real infrastructure, you hit the edges. You want to express:</p>
<ul>
<li>&#8220;This database should have three replicas with point-in-time recovery enabled&#8221; — not a Deployment</li>
<li>&#8220;This TLS certificate for api.example.com should renew 30 days before expiry&#8221; — not a Secret</li>
<li>&#8220;This queue consumer should scale to zero when the queue is empty&#8221; — not a HorizontalPodAutoscaler</li>
</ul>
<p>Before CRDs (pre-2017), the only options were: use ConfigMaps as a poor substitute (no schema, no validation, no dedicated RBAC), or fork Kubernetes and add the resource natively (impractical for everyone outside the core team).</p>
<p>CRDs, introduced as stable in Kubernetes 1.16, solved this by letting you register a new resource type with the API server at runtime — without touching Kubernetes source code, without restarting the API server, without any special access beyond being able to create cluster-scoped resources.</p>
<hr />
<h2 id="the-kubernetes-api-a-brief-mental-model">The Kubernetes API: A Brief Mental Model</h2>
<p>Before CRDs make sense, the API model needs to be clear.</p>
<pre><code class="" data-line="">  KUBERNETES API STRUCTURE

  apiVersion: apps/v1       ← API group (apps) + version (v1)
  kind: Deployment          ← resource type
  metadata:
    name: web               ← instance name
    namespace: default      ← namespace scope
  spec:
    replicas: 3             ← desired state
</code></pre>
<p>Every Kubernetes resource has:<br />
&#8211; A <strong>group</strong> (e.g., <code class="" data-line="">apps</code>, <code class="" data-line="">batch</code>, <code class="" data-line="">networking.k8s.io</code>) — or no group for core resources<br />
&#8211; A <strong>version</strong> (e.g., <code class="" data-line="">v1</code>, <code class="" data-line="">v1beta1</code>)<br />
&#8211; A <strong>kind</strong> (e.g., <code class="" data-line="">Deployment</code>, <code class="" data-line="">Pod</code>)<br />
&#8211; A <strong>scope</strong>: namespaced or cluster-wide</p>
<p>The API server is a registry. Each group/version/kind combination maps to a Go struct that knows how to validate, store, and serve that resource type.</p>
<p>A CRD registers a new entry in that registry. You supply the group, version, kind, and schema. The API server handles everything else — serving it via REST, storing it in etcd, exposing it to <code class="" data-line="">kubectl</code>.</p>
<hr />
<h2 id="what-a-crd-looks-like">What a CRD Looks Like</h2>
<p>Here is the smallest possible CRD — it creates a new <code class="" data-line="">BackupPolicy</code> resource type in the <code class="" data-line="">storage.example.com</code> API group:</p>
<pre><code class="" data-line="">apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: backuppolicies.storage.example.com
spec:
  group: storage.example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                schedule:
                  type: string
                retentionDays:
                  type: integer
  scope: Namespaced
  names:
    plural: backuppolicies
    singular: backuppolicy
    kind: BackupPolicy
    shortNames:
      - bp
</code></pre>
<p>Apply it:</p>
<pre><code class="" data-line="">kubectl apply -f backuppolicy-crd.yaml
</code></pre>
<p>Now create an instance:</p>
<pre><code class="" data-line="">apiVersion: storage.example.com/v1alpha1
kind: BackupPolicy
metadata:
  name: nightly
  namespace: default
spec:
  schedule: &quot;0 2 * * *&quot;
  retentionDays: 30
</code></pre>
<pre><code class="" data-line="">kubectl apply -f nightly-backup.yaml
kubectl get backuppolicies
kubectl get bp            # shortName works
kubectl describe bp nightly
</code></pre>
<p>The API server validates the spec against the schema, stores it in etcd, and returns it via all the standard API endpoints — all without a single line of custom code.</p>
<hr />
<h2 id="crd-vs-built-in-resource-what-is-different">CRD vs Built-In Resource: What Is Different?</h2>
<p>Not much, deliberately.</p>
<table>
<thead>
<tr>
<th>Capability</th>
<th>Built-in resource</th>
<th>Custom resource (CRD)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="" data-line="">kubectl get / describe / delete</code></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>RBAC (Roles, ClusterRoles)</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Watch (informers, events)</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Stored in etcd</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>OpenAPI schema validation</td>
<td>Yes</td>
<td>Yes (you define the schema)</td>
</tr>
<tr>
<td>Admission webhooks</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Status subresource</td>
<td>Yes</td>
<td>Optional (you enable it)</td>
</tr>
<tr>
<td>Scale subresource</td>
<td>Yes</td>
<td>Optional (you enable it)</td>
</tr>
<tr>
<td>Built-in controller behavior</td>
<td>Yes</td>
<td>No — you write the controller</td>
</tr>
</tbody>
</table>
<p>The last row is the critical one. When you create a <code class="" data-line="">Deployment</code>, the deployment controller immediately starts managing ReplicaSets. When you create a <code class="" data-line="">BackupPolicy</code>, nothing happens — until you write and deploy a <strong>controller</strong> that watches <code class="" data-line="">BackupPolicy</code> objects and acts on them.</p>
<p>That controller + the CRD is what people call an <strong>Operator</strong>.</p>
<hr />
<h2 id="a-real-cluster-what-you-actually-see">A Real Cluster: What You Actually See</h2>
<p>Run this on any cluster running cert-manager, Prometheus Operator, or any other tooling:</p>
<pre><code class="" data-line="">kubectl get crds
</code></pre>
<p>Sample output (abbreviated):</p>
<pre><code class="" data-line="">NAME                                                  CREATED AT
certificates.cert-manager.io                          2024-11-01T08:12:00Z
certificaterequests.cert-manager.io                   2024-11-01T08:12:00Z
issuers.cert-manager.io                               2024-11-01T08:12:00Z
clusterissuers.cert-manager.io                        2024-11-01T08:12:00Z
scaledobjects.keda.sh                                 2024-11-01T08:13:00Z
scaledjobs.keda.sh                                    2024-11-01T08:13:00Z
externalsecrets.external-secrets.io                   2024-11-01T08:14:00Z
prometheuses.monitoring.coreos.com                    2024-11-01T08:15:00Z
servicemonitors.monitoring.coreos.com                 2024-11-01T08:15:00Z
</code></pre>
<p>Every tool that ships as a CRD-based system registers its resource types here first. The count often surprises engineers: a production cluster with a typical toolchain easily has 40–80 CRDs.</p>
<p>Check how many are on your cluster:</p>
<pre><code class="" data-line="">kubectl get crds --no-headers | wc -l
</code></pre>
<hr />
<h2 id="how-the-api-server-handles-a-crd">How the API Server Handles a CRD</h2>
<p>When you apply a CRD, the API server does three things:</p>
<pre><code class="" data-line="">  CRD REGISTRATION FLOW

  kubectl apply -f my-crd.yaml
          │
          ▼
  1. API server validates the CRD manifest
     (is the schema valid OpenAPI v3? are names correct?)
          │
          ▼
  2. CRD stored in etcd
     (under /registry/apiextensions.k8s.io/customresourcedefinitions/)
          │
          ▼
  3. New REST endpoints activated immediately:
     GET  /apis/storage.example.com/v1alpha1/namespaces/{ns}/backuppolicies
     POST /apis/storage.example.com/v1alpha1/namespaces/{ns}/backuppolicies
     ...
</code></pre>
<p>From this point, any <code class="" data-line="">kubectl get backuppolicies</code> or API call to those endpoints is handled exactly like a built-in resource call — the API server serves it from etcd, applies RBAC, runs admission webhooks, and returns standard JSON.</p>
<p>No restart required. The new endpoints appear within seconds.</p>
<hr />
<h2 id="the-difference-between-crd-and-cr">The Difference Between CRD and CR</h2>
<p>Two terms that are easily confused:</p>
<ul>
<li><strong>CRD (CustomResourceDefinition)</strong> — the schema/blueprint. There is one CRD per resource type. <code class="" data-line="">certificates.cert-manager.io</code> is a CRD.</li>
<li><strong>CR (Custom Resource)</strong> — an instance of a CRD. Every <code class="" data-line="">Certificate</code> object you create is a custom resource. You can have thousands of CRs per CRD.</li>
</ul>
<pre><code class="" data-line="">  CRD (one)          →  Custom Resource (many)
  ─────────             ─────────────────────
  certificates          web-tls           (namespace: production)
  .cert-manager.io      api-tls           (namespace: production)
                        admin-tls         (namespace: staging)
                        ...
</code></pre>
<p>The CRD is applied once (usually by the tool&#8217;s Helm chart). Custom resources are created by your users, your CI pipeline, or your GitOps system throughout the life of the cluster.</p>
<hr />
<h2 id="where-crds-fit-in-the-kubernetes-extension-model">Where CRDs Fit in the Kubernetes Extension Model</h2>
<p>CRDs are one of three ways to extend Kubernetes:</p>
<pre><code class="" data-line="">  KUBERNETES EXTENSION MECHANISMS

  1. CRDs + Controllers (Operators)
     Add new resource types + behavior
     → cert-manager, KEDA, Argo CD, Crossplane
     Used for: domain-specific abstractions, infrastructure management

  2. Admission Webhooks
     Intercept API requests to validate or mutate objects
     → OPA/Gatekeeper, Kyverno, Istio injection
     Used for: policy enforcement, sidecar injection, defaulting

  3. API Aggregation (AA)
     Register a fully separate API server behind the main API server
     → metrics-server, custom autoscalers
     Used for: when you need non-CRUD semantics (e.g. exec, attach, streaming)
</code></pre>
<p>For 95% of use cases, CRDs + controllers are the right mechanism. API aggregation is complex and only warranted for non-standard API semantics. Admission webhooks are complementary to CRDs, not an alternative.</p>
<hr />
<h2 id="common-mistakes"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Mistakes</h2>
<p><strong>Confusing the CRD with the controller.</strong> The CRD is just a schema registration — it does not execute code. If you apply a CRD but do not deploy its controller, creating custom resources will succeed (the API server accepts them) but nothing will happen. This catches many people the first time they try to use cert-manager by only applying the CRDs without installing the cert-manager controller.</p>
<p><strong>Assuming CRD deletion is safe.</strong> Deleting a CRD deletes all custom resources of that type from etcd. There is no &#8220;are you sure?&#8221; prompt. If you delete the <code class="" data-line="">certificates.cert-manager.io</code> CRD, every <code class="" data-line="">Certificate</code> object in every namespace is gone.</p>
<p><strong>Treating CRDs as ConfigMap replacements.</strong> Some teams store configuration in CRDs purely to get schema validation. This works, but without a controller, the custom resources are inert data. If you only need configuration storage with validation, a CRD is viable — just be explicit that there is no reconciliation loop.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<pre><code class="" data-line=""># List all CRDs in the cluster
kubectl get crds

# Inspect a specific CRD&#039;s schema
kubectl get crd certificates.cert-manager.io -o yaml

# List all custom resources of a type
kubectl get certificates -A

# Get details on a specific custom resource
kubectl describe certificate web-tls -n production

# Delete a CRD (WARNING: deletes all instances)
kubectl delete crd backuppolicies.storage.example.com

# Check if a CRD is established (ready to use)
kubectl get crd backuppolicies.storage.example.com \
  -o jsonpath=&#039;{.status.conditions[?(@.type==&quot;Established&quot;)].status}&#039;
# Returns: True
</code></pre>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>A Kubernetes CRD registers a new resource type with the API server — no source code changes, no restart required</li>
<li>Custom resources behave identically to built-in resources: <code class="" data-line="">kubectl</code>, RBAC, watches, etcd, admission webhooks all work the same way</li>
<li>The CRD is just the schema; a controller gives custom resources behavior — together they form an Operator</li>
<li>Every production cluster running modern tooling already uses dozens of CRDs</li>
<li>Deleting a CRD deletes all its instances — treat CRDs as production-critical objects</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p><a href="/kubernetes-custom-resources-examples/">EP02: CRDs You Already Use</a> makes this concrete before we go deeper — we walk through cert-manager&#8217;s <code class="" data-line="">Certificate</code>, KEDA&#8217;s <code class="" data-line="">ScaledObject</code>, and External Secrets&#8217; <code class="" data-line="">ExternalSecret</code> as working examples, so you understand what a well-designed CRD looks like from a user&#8217;s perspective before you design your own.</p>
<p>Get EP02 in your inbox when it publishes → <a href="https://linuxcent.com">subscribe at linuxcent.com</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&amp;linkname=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fwhat-is-kubernetes-crd%2F&#038;title=What%20Is%20a%20Kubernetes%20CRD%3F%20How%20Custom%20Resources%20Extend%20the%20API" data-a2a-url="https://linuxcent.com/what-is-kubernetes-crd/" data-a2a-title="What Is a Kubernetes CRD? How Custom Resources Extend the API"></a></p><p>The post <a href="https://linuxcent.com/what-is-kubernetes-crd/">What Is a Kubernetes CRD? How Custom Resources Extend the API</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/what-is-kubernetes-crd/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1675</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-09-02 10:44:32 by W3 Total Cache
-->