<?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>Crossplane Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/crossplane/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/crossplane/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Fri, 10 Jul 2026 15:37: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>Crossplane Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/crossplane/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>Crossplane vs Terraform: Composition vs HCL for Infrastructure as Code</title>
		<link>https://linuxcent.com/crossplane-vs-terraform/</link>
					<comments>https://linuxcent.com/crossplane-vs-terraform/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 23 Jul 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Kubernetes Ecosystem]]></category>
		<category><![CDATA[Crossplane]]></category>
		<category><![CDATA[Infrastructure as Code]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Terraform]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=2255</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"> 5</span> <span class="rt-label rt-postfix">minutes</span></span>Compare Crossplane's Kubernetes-native composition model to Terraform's HCL state model — and get a clear recommendation, not a neutral list.</p>
<p>The post <a href="https://linuxcent.com/crossplane-vs-terraform/">Crossplane vs Terraform: Composition vs HCL for Infrastructure as Code</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"> 5</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 Ecosystem: From User to Contributor, Episode 7</em><br />
<a href="/crossplane-universal-control-plane/">← EP06: Crossplane</a> · <strong>EP07: Crossplane vs Terraform</strong> · <a href="/karpenter-node-provisioning/">EP08: Karpenter →</a></p>
<p><strong>11 min read</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Crossplane vs Terraform is fundamentally a continuous-reconciliation model against a plan/apply model — not just two different syntaxes for the same idea</li>
<li>Crossplane needs a live Kubernetes cluster to run at all; Terraform needs nothing but a state file and network access to the providers it&#8217;s calling</li>
<li>Terraform&#8217;s provider registry is a decade deep and covers services Crossplane&#8217;s younger ecosystem hasn&#8217;t reached yet — SaaS tools, monitoring platforms, and services with no cloud-infrastructure angle at all</li>
<li>Crossplane&#8217;s Compositions give app teams a genuinely self-service, in-cluster API; Terraform&#8217;s modules give infra teams reusable code, but consuming a module still means running Terraform yourself</li>
<li>Recommendation: many real platform teams use both — Terraform (or CAPI, EP05) to bootstrap the cluster and its surrounding VPC/networking, then Crossplane running inside that cluster for the self-service, app-team-facing layer</li>
<li>Contribution opportunity: Crossplane&#8217;s provider coverage gap against Terraform&#8217;s registry is real, specific, and a legitimate place to build a brand-new provider</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">TERRAFORM                                   CROSSPLANE
──────────                                   ──────────
terraform plan                               kubectl apply -f resource.yaml
  │  (shows what WOULD change)                    │
  ▼                                                ▼
terraform apply                              Crossplane controller reconciles
  │  (changes happen once, here)                   │  (continuously, forever,
  ▼                                                 │   not just at apply time)
State file (local or remote backend)               ▼
tracks what Terraform created                 Kubernetes etcd IS the state —
                                                the CR&#039;s status field tracks
No live cluster or control                     sync state
plane required to run this
                                               Requires a running Kubernetes
                                               cluster as the control plane
</code></pre>
<p>Crossplane vs Terraform is best understood through that control-flow difference first, before comparing any specific feature: Terraform changes things at discrete moments you trigger; Crossplane&#8217;s controllers are always watching, always correcting drift, for as long as the cluster runs.</p>
<hr />
<h2 id="the-fundamental-model-difference-continuous-reconciliation-vs-planapply">The Fundamental Model Difference: Continuous Reconciliation vs Plan/Apply</h2>
<p>Terraform&#8217;s model gives you an explicit review step — <code class="" data-line="">terraform plan</code> shows exactly what will change before anything does, and nothing changes again until you run <code class="" data-line="">apply</code> a second time. Crossplane&#8217;s model (covered in EP06) has no equivalent pause: once a Managed Resource or Composition claim exists, Crossplane&#8217;s controllers reconcile it toward the desired state continuously, including reverting manual out-of-band changes automatically.</p>
<p>Neither is objectively better — they&#8217;re suited to different operating assumptions. Terraform&#8217;s model fits teams who want a deliberate, reviewed change process. Crossplane&#8217;s fits teams who want infrastructure to behave like every other Kubernetes-native resource: self-healing, always converging, no separate &#8220;did anyone remember to re-apply&#8221; step.</p>
<hr />
<h2 id="state-management-etcd-crds-vs-terraform-state-files">State Management: etcd + CRDs vs Terraform State Files</h2>
<pre><code class="" data-line=""># Terraform: state lives in a file (local or remote — S3, Terraform Cloud, etc.)
$ terraform state list
aws_s3_bucket.uploads
aws_db_instance.main

# Crossplane: &quot;state&quot; is just the live cluster&#039;s etcd — the CR&#039;s own status
$ kubectl get bucket uploads -o jsonpath=&#039;{.status.conditions}&#039;
[{&quot;type&quot;:&quot;Ready&quot;,&quot;status&quot;:&quot;True&quot;},{&quot;type&quot;:&quot;Synced&quot;,&quot;status&quot;:&quot;True&quot;}]
</code></pre>
<p>Terraform&#8217;s state file is a single point of coordination that has to be locked correctly for concurrent runs to be safe — a well-understood but real operational concern (remote state backends, state locking, occasional manual state surgery after a botched apply). Crossplane sidesteps a separate state file entirely, but that means the health of your Kubernetes cluster&#8217;s etcd <em>is</em> the health of your infrastructure&#8217;s state — a different, not smaller, operational responsibility.</p>
<hr />
<h2 id="composition-vs-modules-reusable-infrastructure-patterns-compared">Composition vs Modules: Reusable Infrastructure Patterns Compared</h2>
<p>Terraform modules are reusable code that whoever runs Terraform includes in their own configuration — genuinely reusable, but still something each consumer runs themselves. Crossplane Compositions (EP06) are reusable <em>inside the cluster</em> — an app team doesn&#8217;t run anything, they just create a custom resource the platform team already defined, and Crossplane&#8217;s controllers do the rest without the app team ever touching Terraform or Crossplane&#8217;s own tooling directly.</p>
<p>That&#8217;s the real practical difference for organizational self-service: Compositions remove the &#8220;app team needs to know how to run our IaC tool&#8221; step entirely. Modules still require the consumer to run Terraform, even if they didn&#8217;t write the module.</p>
<hr />
<h2 id="ecosystem-maturity-terraforms-decade-deep-provider-registry-vs-crossplanes-younger-one">Ecosystem Maturity: Terraform&#8217;s Decade-Deep Provider Registry vs Crossplane&#8217;s Younger One</h2>
<p>Terraform&#8217;s provider registry covers not just the major clouds but a long tail of SaaS platforms, monitoring tools, DNS providers, and internal enterprise systems that have no &#8220;cloud infrastructure&#8221; angle at all — a decade of community and vendor-contributed providers. Crossplane&#8217;s provider ecosystem, while actively growing and covering the major clouds thoroughly, has real, documented gaps once you look past core compute/storage/networking/database resources into more specialized or less common services.</p>
<hr />
<h2 id="the-recommendation-which-one-and-when-to-use-both">The Recommendation: Which One, and When to Use Both</h2>
<p><strong>If your platform team is Kubernetes-native and wants to offer app teams a true self-service infrastructure API without teaching them a separate IaC tool:</strong> Crossplane. That&#8217;s the specific problem its Composition model solves better than anything Terraform offers.</p>
<p><strong>If you need broad provider coverage beyond core cloud infrastructure, or you don&#8217;t want infrastructure lifecycle tied to a live Kubernetes control plane&#8217;s uptime:</strong> Terraform. Its registry depth and its independence from any running cluster are real advantages Crossplane doesn&#8217;t currently match.</p>
<p><strong>The honest answer for a lot of real platform teams is both, at different layers.</strong> Use Terraform (or Cluster API, EP05) to bootstrap the Kubernetes cluster itself and its surrounding cloud networking — the layer that has to exist before Crossplane can run at all — then run Crossplane inside that cluster for the ongoing, self-service, app-team-facing infrastructure requests. This isn&#8217;t a compromise; it&#8217;s matching each tool to the layer it&#8217;s actually better suited for.</p>
<hr />
<h2 id="production-gotchas"><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;" /> Production Gotchas</h2>
<p><strong>Don&#8217;t manage the same cloud resource with both Terraform and Crossplane simultaneously.</strong> Both tools will detect the other&#8217;s changes as drift and fight to revert them — pick one owner per resource, even when both tools are in use across your stack at different layers.</p>
<p><strong>Terraform&#8217;s plan/apply gives you a review window Crossplane doesn&#8217;t — build your own review gate if you need one with Crossplane</strong> (a PR-based GitOps flow with required approval before a claim manifest merges is the common substitute).</p>
<p><strong>Crossplane&#8217;s continuous reconciliation means a broken provider or a cloud API outage shows up as a stuck <code class="" data-line="">Synced: False</code> condition, not a failed one-time command</strong> — monitoring needs to watch for stuck conditions over time, not just command exit codes the way Terraform CI pipelines typically do.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<table>
<thead>
<tr>
<th></th>
<th>Terraform</th>
<th>Crossplane</th>
</tr>
</thead>
<tbody>
<tr>
<td>Change model</td>
<td>Plan → Apply (explicit)</td>
<td>Continuous reconciliation</td>
</tr>
<tr>
<td>Requires a live cluster</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>State</td>
<td>State file (local/remote)</td>
<td>Kubernetes etcd + CR status</td>
</tr>
<tr>
<td>Reusable patterns</td>
<td>Modules (you still run them)</td>
<td>Compositions (app team just creates a claim)</td>
</tr>
<tr>
<td>Provider breadth</td>
<td>Very broad, decade-deep</td>
<td>Growing, strong on core cloud, gaps elsewhere</td>
</tr>
<tr>
<td>Manual drift</td>
<td>Detected at next <code class="" data-line="">plan</code>, not auto-reverted</td>
<td>Auto-reverted on next reconcile</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="contribution-opportunity-building-a-missing-crossplane-provider">Contribution Opportunity: Building a Missing Crossplane Provider</h2>
<p><strong>The limitation:</strong> For a meaningful number of services Terraform has supported for years — smaller SaaS platforms, specialized monitoring tools, niche infrastructure services — there&#8217;s no Crossplane provider equivalent yet. Anyone wanting to manage that service the Crossplane way currently can&#8217;t, full stop.</p>
<p><strong>Why it&#8217;s hard to fix:</strong> Building a new provider means implementing a real API client, defining CRD schemas that faithfully map the service&#8217;s actual parameters, and maintaining it as that service&#8217;s API evolves — real, ongoing engineering commitment, not a one-time script. That&#8217;s exactly why the ecosystem&#8217;s provider list still trails Terraform&#8217;s, despite Crossplane&#8217;s core reconciliation engine being mature: the core is one thing to maintain, but each provider is its own ongoing surface area.</p>
<p><strong>What a contribution-shaped fix looks like:</strong> Crossplane&#8217;s <code class="" data-line="">provider-template</code> repository exists specifically to make starting a new provider tractable — it scaffolds the boilerplate (code generation, CRD structure, controller wiring) so a new provider author focuses on the actual API mapping, not plumbing. Picking one service you already use via Terraform that has no Crossplane equivalent, and building a minimal provider covering just the 2-3 resource types you actually need, is a real, bounded, achievable contribution — and one the Crossplane community actively wants, given how directly it grows the ecosystem.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Crossplane&#8217;s continuous reconciliation and Terraform&#8217;s plan/apply are different operating models, not different syntaxes for the same thing — pick based on which review/change process fits your team</li>
<li>Crossplane requires a live cluster to function at all; Terraform doesn&#8217;t, which matters for bootstrapping order</li>
<li>Compositions remove the &#8220;app team has to run our IaC tool&#8221; step that Terraform modules still require</li>
<li>Terraform&#8217;s provider registry breadth remains a real advantage for anything beyond core cloud infrastructure</li>
<li>Many real platform teams run both at different layers — Terraform/CAPI to bootstrap the cluster, Crossplane inside it for self-service — and that&#8217;s a legitimate architecture, not indecision</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>Everything so far in this series has been about provisioning clusters and the infrastructure around them. EP08 shifts to what happens inside an already-running cluster when pods can&#8217;t be scheduled: Karpenter&#8217;s just-in-time node provisioning, and why it replaced the node-group model most teams started with.</p>
<p><em>Next: <a href="/karpenter-node-provisioning/">EP08 — Karpenter: Just-in-Time Node Provisioning for Kubernetes</a></em></p>
<p>Get EP08 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&amp;linkname=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" 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%2Fcrossplane-vs-terraform%2F&#038;title=Crossplane%20vs%20Terraform%3A%20Composition%20vs%20HCL%20for%20Infrastructure%20as%20Code" data-a2a-url="https://linuxcent.com/crossplane-vs-terraform/" data-a2a-title="Crossplane vs Terraform: Composition vs HCL for Infrastructure as Code"></a></p><p>The post <a href="https://linuxcent.com/crossplane-vs-terraform/">Crossplane vs Terraform: Composition vs HCL for Infrastructure as Code</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/crossplane-vs-terraform/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2255</post-id>	</item>
		<item>
		<title>Crossplane: Kubernetes as the Universal Control Plane</title>
		<link>https://linuxcent.com/crossplane-universal-control-plane/</link>
					<comments>https://linuxcent.com/crossplane-universal-control-plane/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Tue, 21 Jul 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Kubernetes Ecosystem]]></category>
		<category><![CDATA[Crossplane]]></category>
		<category><![CDATA[Infrastructure as Code]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Platform Engineering]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=2252</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"> 5</span> <span class="rt-label rt-postfix">minutes</span></span>See how Crossplane turns Kubernetes into a control plane for any cloud resource — and exactly what that composition model costs you operationally.</p>
<p>The post <a href="https://linuxcent.com/crossplane-universal-control-plane/">Crossplane: Kubernetes as the Universal Control Plane</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"> 5</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 Ecosystem: From User to Contributor, Episode 6</em><br />
<a href="/cluster-api-declarative-lifecycle/">← EP05: Cluster API</a> · <strong>EP06: Crossplane</strong> · <a href="/crossplane-vs-terraform/">EP07: Crossplane vs Terraform →</a></p>
<p><strong>12 min read</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Crossplane extends the exact reconciliation pattern EP05 covered for cluster infrastructure to <em>any</em> cloud resource — an S3 bucket, an RDS instance, a DNS record all become Kubernetes CRDs, continuously reconciled</li>
<li><strong>Managed Resources</strong> represent one real cloud resource each; <strong>Compositions</strong> bundle several Managed Resources behind a single, simpler custom API a platform team defines and app teams consume</li>
<li><strong>Composition Functions</strong> are Crossplane&#8217;s newer, more flexible replacement for its older YAML-based patch-and-transform templating — real code (Go, Python, or others) instead of declarative patches</li>
<li>Crossplane continuously reconciles like any Kubernetes controller — a manual change to a cloud resource outside Crossplane gets reverted on the next reconcile loop, which is a real surprise for teams used to Terraform&#8217;s plan/apply model</li>
<li>Provider CRD counts can bloat a cluster&#8217;s etcd significantly — this drove the ecosystem&#8217;s move toward smaller, split &#8220;provider families&#8221; instead of one monolithic provider per cloud</li>
<li>Contribution opportunity: several providers still haven&#8217;t migrated to the family-split pattern — a real, currently-tracked, achievable upstream contribution</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">App team writes:                    Platform team defined this Composition
                                     once, behind the scenes:
apiVersion: platform.example.com/v1
kind: Database                       XRD &quot;Database&quot; ─── composes ───┐
metadata:                                                             │
  name: my-app-db                                                     ▼
spec:                                                        ┌────────────────┐
  size: small                                                │ RDSInstance    │
                                                                │ SecurityGroup  │
   │                                                            │ ParameterGroup │
   │ app team never sees                                        └────────────────┘
   │ or touches these three                                     each a real Managed
   ▼                                                            Resource, a real
Crossplane reconciles all three,                                cloud API call
continuously, forever
</code></pre>
<p>Crossplane&#8217;s pitch as a universal control plane is literal: instead of app teams filing tickets or writing their own Terraform for a database, they request a <code class="" data-line="">Database</code> — a custom API the platform team designed — and Crossplane&#8217;s controllers translate that into the actual RDS instance, security group, and parameter group underneath, then keep reconciling all three toward the declared state indefinitely.</p>
<hr />
<h2 id="managed-resources-cloud-infrastructure-as-kubernetes-crds">Managed Resources: Cloud Infrastructure as Kubernetes CRDs</h2>
<pre><code class="" data-line="">$ kubectl apply -f - &lt;&lt;EOF
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: app-uploads-prod
spec:
  forProvider:
    region: us-east-1
  providerConfigRef:
    name: aws-prod
EOF

$ kubectl get bucket app-uploads-prod
NAME               READY   SYNCED   AGE
app-uploads-prod   True    True     30s
#                  ^^^^    ^^^^^^ — READY: resource exists and is healthy
#                          SYNCED: Crossplane&#039;s last reconcile succeeded
</code></pre>
<p>Every field under <code class="" data-line="">forProvider</code> maps directly to that cloud API&#8217;s actual parameters — this is a thin, honest translation layer, not an abstraction hiding what&#8217;s actually being created. <code class="" data-line="">READY</code>/<code class="" data-line="">SYNCED</code> becoming <code class="" data-line="">True</code> means an actual S3 bucket now exists in that AWS account, exactly as declared.</p>
<hr />
<h2 id="compositions-and-xrds-building-your-own-abstract-platform-api">Compositions and XRDs: Building Your Own Abstract Platform API</h2>
<p>This is Crossplane&#8217;s real differentiator over just using individual Managed Resources directly:</p>
<pre><code class="" data-line=""># The platform team defines the abstract API app teams will see
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xdatabases.platform.example.com
spec:
  group: platform.example.com
  names:
    kind: XDatabase
    plural: xdatabases
  claimNames:
    kind: Database        # ← this is what app teams actually create
    plural: databases
  versions:
  - name: v1
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              size: {type: string, enum: [&quot;small&quot;, &quot;medium&quot;, &quot;large&quot;]}
</code></pre>
<p>App teams interact only with the simple <code class="" data-line="">Database</code> claim shown in the Big Picture diagram above. The <code class="" data-line="">Composition</code> resource (not shown here for brevity) is what actually maps <code class="" data-line="">size: small</code> to a specific RDS instance class, storage size, and backup configuration — the platform team&#8217;s opinions, encoded once, consumed self-service by every app team afterward.</p>
<hr />
<h2 id="composition-functions-crossplanes-newer-more-flexible-approach">Composition Functions: Crossplane&#8217;s Newer, More Flexible Approach</h2>
<p>Older Crossplane Compositions used a YAML-based &#8220;patch and transform&#8221; templating language to map the abstract API&#8217;s fields onto Managed Resource fields — functional, but limited for anything beyond straightforward field mapping. <strong>Composition Functions</strong> replace that with actual executable code:</p>
<pre><code class="" data-line="">$ crossplane beta render xr.yaml composition.yaml functions.yaml
---
apiVersion: rds.aws.upbound.io/v1alpha1
kind: Instance
metadata:
  name: my-app-db-instance
spec:
  forProvider:
    instanceClass: db.t3.micro   # ← computed by real Go logic based on
                                  #   spec.size, not a static YAML patch
    engine: postgres
</code></pre>
<p>Composition Functions run as small, packaged pieces of logic (often distributed as OCI images) that Crossplane&#8217;s engine invokes during reconciliation — giving platform teams real conditionals, loops, and validation instead of the older templating language&#8217;s more limited patch syntax.</p>
<hr />
<h2 id="providers-and-the-provider-ecosystem">Providers and the Provider Ecosystem</h2>
<p>Each cloud&#8217;s resources are supplied by a separate <strong>provider</strong> — <code class="" data-line="">provider-aws</code>, <code class="" data-line="">provider-gcp</code>, <code class="" data-line="">provider-azure</code>, and increasingly split into smaller <strong>provider families</strong> (<code class="" data-line="">provider-aws-s3</code>, <code class="" data-line="">provider-aws-rds</code>, etc.) rather than one enormous provider per cloud:</p>
<pre><code class="" data-line="">$ kubectl get providers
NAME                   INSTALLED   HEALTHY   AGE
provider-aws-s3        True        True      10d
provider-aws-rds       True        True      10d
#         ^^^^^^ — installing only the families you actually use, instead
#                  of one monolithic provider-aws with every AWS service&#039;s
#                  CRDs installed regardless of whether you use them
</code></pre>
<p>The family split exists specifically because a single monolithic cloud provider can register thousands of CRDs — a real, measurable strain on a cluster&#8217;s etcd and API server that the ecosystem is still in the process of migrating away from.</p>
<hr />
<h2 id="production-gotchas"><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;" /> Production Gotchas</h2>
<p><strong>Crossplane reconciles continuously — a manual change to a cloud resource outside Crossplane gets reverted on the next loop.</strong> Teams coming from Terraform&#8217;s plan/apply model, where nothing changes until you explicitly run <code class="" data-line="">apply</code> again, are frequently surprised the first time a manual &#8220;quick fix&#8221; in the AWS console gets silently undone minutes later.</p>
<p><strong>Monolithic providers can register thousands of CRDs, and that has a real, measurable etcd and API-server cost.</strong> If you&#8217;re on an older, non-family provider version and seeing API server memory pressure, check CRD count before assuming it&#8217;s an unrelated capacity issue.</p>
<p><strong>Deleting a Composition&#8217;s underlying claim doesn&#8217;t always tear down cleanly if finalizers on the Managed Resources are stuck</strong> — a Managed Resource that failed to delete cleanly from the cloud side (a non-empty S3 bucket, for instance) will block the whole claim&#8217;s deletion until that&#8217;s resolved manually.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<pre><code class="" data-line="">kubectl get managed                        # every Managed Resource, all providers
kubectl get compositeresourcedefinitions   # XRDs — the abstract APIs defined
kubectl get compositions                   # the mapping logic behind each XRD
kubectl get providers                       # installed providers + health
crossplane beta render &lt;xr&gt; &lt;comp&gt; &lt;fns&gt;    # render a Composition locally, no cluster needed
kubectl describe &lt;managed-resource-kind&gt; &lt;name&gt;   # sync status + underlying cloud errors
</code></pre>
<hr />
<h2 id="contribution-opportunity-migrating-providers-to-the-family-pattern">Contribution Opportunity: Migrating Providers to the Family Pattern</h2>
<p><strong>The limitation:</strong> Not every Crossplane provider has migrated from the older, monolithic-per-cloud model to the smaller &#8220;provider family&#8221; pattern that registers only the CRDs for services actually in use. Clusters running an un-migrated provider carry the etcd and API-server overhead of thousands of unused CRDs, and this is a known, actively-discussed problem in the Crossplane community — not a hypothetical one.</p>
<p><strong>Why it&#8217;s hard to fix:</strong> Splitting a monolithic provider into families isn&#8217;t a mechanical find-and-replace — it means restructuring code generation, versioning, and release processes for every resource type the provider covers, while keeping a migration path that doesn&#8217;t break existing users who depend on the old provider&#8217;s CRDs. It&#8217;s real, unglamorous engineering work that has to happen provider-by-provider, cloud-by-cloud, and each provider&#8217;s maintainer bandwidth varies.</p>
<p><strong>What a contribution-shaped fix looks like:</strong> The Crossplane and Upbound-maintained provider repositories publicly track which providers still need family-splitting — this is documented, wanted work, not a gap you&#8217;d have to go discover yourself. A concrete starting contribution: pick one still-monolithic provider (checking the project&#8217;s own tracking issues for an unclaimed one), and work through the documented family-split process the already-migrated providers (like <code class="" data-line="">provider-aws</code>) used as a reference implementation. This is real upstream OSS work with an existing template to follow, not a design problem you have to solve from scratch.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Crossplane&#8217;s Managed Resources make individual cloud resources real Kubernetes CRDs, continuously reconciled rather than applied once</li>
<li>Compositions and XRDs are the actual value proposition: platform teams define a simple, opinionated API once; app teams self-serve against it without needing to know what&#8217;s underneath</li>
<li>Composition Functions replace older YAML patch-and-transform templating with real executable logic — a genuinely evolving, more flexible part of the project</li>
<li>Continuous reconciliation means manual out-of-band changes get reverted — a real behavioral difference from Terraform&#8217;s plan/apply model, not just a implementation detail</li>
<li>The provider family migration is documented, wanted, achievable contribution work — not a gap you&#8217;d need to discover on your own</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>Crossplane&#8217;s composition model and Terraform&#8217;s HCL module model solve the same underlying problem — reusable, parameterized infrastructure definitions — from genuinely different architectural starting points. EP07 puts them side by side and gives a clear recommendation for which fits which team.</p>
<p><em>Next: <a href="/crossplane-vs-terraform/">EP07 — Crossplane vs Terraform: Composition vs HCL for Infrastructure as Code</a></em></p>
<p>Get EP07 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&amp;linkname=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" 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%2Fcrossplane-universal-control-plane%2F&#038;title=Crossplane%3A%20Kubernetes%20as%20the%20Universal%20Control%20Plane" data-a2a-url="https://linuxcent.com/crossplane-universal-control-plane/" data-a2a-title="Crossplane: Kubernetes as the Universal Control Plane"></a></p><p>The post <a href="https://linuxcent.com/crossplane-universal-control-plane/">Crossplane: Kubernetes as the Universal Control Plane</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/crossplane-universal-control-plane/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2252</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-08-25 09:10:30 by W3 Total Cache
-->