<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jamil Shaikh</title>
    <description>The latest articles on Forem by Jamil Shaikh (@justasflash).</description>
    <link>https://forem.com/justasflash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F160709%2F321d65aa-5b75-4986-b2e8-651f847f30b2.jpg</url>
      <title>Forem: Jamil Shaikh</title>
      <link>https://forem.com/justasflash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/justasflash"/>
    <language>en</language>
    <item>
      <title>🚀 Building a GitOps Infrastructure Pipeline with Crossplane and Argo CD</title>
      <dc:creator>Jamil Shaikh</dc:creator>
      <pubDate>Fri, 29 Aug 2025 21:12:55 +0000</pubDate>
      <link>https://forem.com/justasflash/building-a-gitops-infrastructure-pipeline-with-crossplane-and-argo-cd-431</link>
      <guid>https://forem.com/justasflash/building-a-gitops-infrastructure-pipeline-with-crossplane-and-argo-cd-431</guid>
      <description>&lt;p&gt;&lt;em&gt;From manual kubectl commands to fully automated infrastructure management - here's how I built a production-ready GitOps pipeline&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I built a complete GitOps infrastructure management system using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎯 &lt;strong&gt;Argo CD&lt;/strong&gt; for GitOps automation&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Crossplane&lt;/strong&gt; for infrastructure provisioning&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;App-of-Apps pattern&lt;/strong&gt; for scalable application management&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;MetalLB&lt;/strong&gt; as the infrastructure example&lt;/li&gt;
&lt;li&gt;🎭 &lt;strong&gt;Sync waves&lt;/strong&gt; for dependency management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Infrastructure changes now happen through Git commits, with full automation and zero manual intervention.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Solved
&lt;/h2&gt;

&lt;p&gt;Managing Kubernetes infrastructure traditionally sucks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The old way - manual and error-prone&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; metallb-config.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; ingress-controller.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; monitoring-stack.yaml
&lt;span class="c"&gt;# Oh no! Order matters... 💥&lt;/span&gt;
&lt;span class="c"&gt;# Which version is running in production? 🤷‍♂️&lt;/span&gt;
&lt;span class="c"&gt;# Who made that change? 🕵️‍♂️&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted infrastructure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Lives in Git (version controlled)&lt;/li&gt;
&lt;li&gt;✅ Deploys automatically (no manual steps)&lt;/li&gt;
&lt;li&gt;✅ Handles dependencies (no ordering issues)&lt;/li&gt;
&lt;li&gt;✅ Self-heals (drift detection &amp;amp; correction)&lt;/li&gt;
&lt;li&gt;✅ Provides audit trails (who, what, when)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Solution Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph LR
    A[Git Commit] --&amp;gt; B[Argo CD]
    B --&amp;gt; C[Crossplane]
    C --&amp;gt; D[Kubernetes Resources]
    B --&amp;gt; E[Sync Waves]
    E --&amp;gt; F[Ordered Deployment]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 Step 1: App-of-Apps Pattern
&lt;/h2&gt;

&lt;p&gt;Instead of managing 50+ individual Argo CD applications, I use the &lt;strong&gt;App-of-Apps pattern&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# One app to rule them all&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;homelab-root&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/jamilshaikh07/homelab-gitops.git&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps&lt;/span&gt;  &lt;span class="c1"&gt;# 👈 All child apps live here&lt;/span&gt;
  &lt;span class="na"&gt;syncPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;automated&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;prune&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;      &lt;span class="c1"&gt;# 🗑️ Clean up deleted resources&lt;/span&gt;
      &lt;span class="na"&gt;selfHeal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# 🔧 Fix manual changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One root app manages everything&lt;/li&gt;
&lt;li&gt;New apps = just add YAML files&lt;/li&gt;
&lt;li&gt;Automatic discovery and deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Step 2: Crossplane for Infrastructure
&lt;/h2&gt;

&lt;p&gt;Crossplane lets me manage infrastructure through Kubernetes APIs. Here's the magic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instead of direct kubectl apply...&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.crossplane.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Object&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metallb-ipaddresspool&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;  &lt;span class="c1"&gt;# 👈 Depends on provider&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;providerConfigRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;in-cluster&lt;/span&gt;
  &lt;span class="na"&gt;forProvider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;manifest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metallb.io/v1beta1&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IPAddressPool&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;homelab-pool&lt;/span&gt;
        &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metallb-system&lt;/span&gt;
      &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;10.20.0.81-10.20.0.99&lt;/span&gt;  &lt;span class="c1"&gt;# 🎯 My LoadBalancer IP range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Crossplane Objects?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;Continuous reconciliation&lt;/strong&gt; (drift detection)&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Rich status reporting&lt;/strong&gt; (health, errors)&lt;/li&gt;
&lt;li&gt;🎭 &lt;strong&gt;Dependency management&lt;/strong&gt; (waits for providers)&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;RBAC integration&lt;/strong&gt; (secure access)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎭 Step 3: Sync Waves for Dependencies
&lt;/h2&gt;

&lt;p&gt;Order matters in infrastructure! I use sync waves to ensure proper sequencing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Wave -1: Root app-of-apps&lt;/span&gt;
&lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-1"&lt;/span&gt;

&lt;span class="c1"&gt;# Wave 0: Install Crossplane providers&lt;/span&gt;
&lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0"&lt;/span&gt;

&lt;span class="c1"&gt;# Wave 1: Provider configs + RBAC&lt;/span&gt;
&lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;

&lt;span class="c1"&gt;# Wave 2: Infrastructure resources&lt;/span&gt;
&lt;span class="na"&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; No more "CRD not found" or "provider not ready" errors! 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Step 4: RBAC - The Critical Missing Piece
&lt;/h2&gt;

&lt;p&gt;Crossplane needs permissions to manage your infrastructure. This is often overlooked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-provider-kubernetes-metallb&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metallb.io"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ipaddresspools"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;l2advertisements"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Bind to the provider's service account&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-provider-kubernetes-metallb&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-provider-kubernetes-metallb&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provider-kubernetes-xxxxx&lt;/span&gt;  &lt;span class="c1"&gt;# 👈 Get from kubectl&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Restart provider pods after RBAC changes!&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Testing the Complete Pipeline
&lt;/h2&gt;

&lt;p&gt;Time for the moment of truth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Bootstrap (one-time manual step)&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; argocd/app-of-apps.yaml

&lt;span class="c"&gt;# 2. Check GitOps automation&lt;/span&gt;
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd get applications
NAME                             SYNC STATUS   HEALTH STATUS
crossplane-provider-kubernetes   Synced        Healthy ✅
homelab-root                     Synced        Healthy ✅
metallb-config                   Synced        Healthy ✅

&lt;span class="c"&gt;# 3. Verify infrastructure was created&lt;/span&gt;
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; metallb-system get ipaddresspools.metallb.io
NAME           ADDRESSES
homelab-pool   &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.20.0.81-10.20.0.99"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; ✅

&lt;span class="c"&gt;# 4. Test LoadBalancer functionality&lt;/span&gt;
kubectl create deployment nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
kubectl expose deployment nginx &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80

kubectl get svc nginx
NAME    TYPE           EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;
nginx   LoadBalancer   10.20.0.82    80:30114/TCP ✅

&lt;span class="c"&gt;# 5. Verify connectivity&lt;/span&gt;
curl http://10.20.0.82
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&lt;span class="c"&gt;# 🎉 SUCCESS!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It works!&lt;/strong&gt; Infrastructure deployed entirely through GitOps! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  📁 Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;homelab-gitops/
├── argocd/
│   └── app-of-apps.yaml              # 🏠 Root application
├── apps/
│   ├── crossplane-provider-kubernetes-app.yaml
│   └── metallb-config-app.yaml       # 👶 Child applications
├── crossplane/
│   └── provider-kubernetes/
│       ├── provider.yaml             # ⚡ Crossplane provider
│       ├── providerconfig.yaml       # ⚙️ Configuration
│       └── rbac.yaml                 # 🔐 Permissions
└── metallb/
    ├── metallb-ipaddresspool.yaml    # 🌐 Infrastructure
    └── metallb-l2advertisement.yaml  # 📦 Resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 Key Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;API Versions Are Critical&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Different provider versions use different APIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# v0.13.0 uses v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.crossplane.io/v1alpha1&lt;/span&gt;

&lt;span class="c1"&gt;# Newer versions use v1alpha2  &lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.crossplane.io/v1alpha2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Bootstrap is Still Manual&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even with full GitOps, you need one manual step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; argocd/app-of-apps.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, everything else is automated!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;RBAC Debugging&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If Crossplane objects stay "NotReady":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check provider permissions&lt;/span&gt;
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; crossplane-system describe object metallb-ipaddresspool

&lt;span class="c"&gt;# Common fix: restart provider after RBAC changes&lt;/span&gt;
kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; crossplane-system delete pod &lt;span class="nt"&gt;-l&lt;/span&gt; pkg.crossplane.io/provider&lt;span class="o"&gt;=&lt;/span&gt;provider-kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. &lt;strong&gt;YAML Formatting Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Watch your indentation! This kept my root app OutOfSync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Wrong&lt;/span&gt;
&lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes.default.svc&lt;/span&gt;
&lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-system&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Correct  &lt;/span&gt;
&lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes.default.svc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;crossplane-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 What's Next?
&lt;/h2&gt;

&lt;p&gt;This foundation scales to manage ANY infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Database clusters&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PostgreSQLCluster&lt;/span&gt;

&lt;span class="c1"&gt;# Service meshes  &lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Istio&lt;/span&gt;

&lt;span class="c1"&gt;# Monitoring stacks&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PrometheusStack&lt;/span&gt;

&lt;span class="c1"&gt;# Certificate management&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIssuer&lt;/span&gt;

&lt;span class="c1"&gt;# Storage solutions&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StorageClass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The pattern stays the same:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define in YAML&lt;/li&gt;
&lt;li&gt;Commit to Git
&lt;/li&gt;
&lt;li&gt;Argo CD syncs automatically&lt;/li&gt;
&lt;li&gt;Crossplane provisions infrastructure&lt;/li&gt;
&lt;li&gt;Profit! 💰&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎉 Results
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual &lt;code&gt;kubectl&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;Configuration drift&lt;/li&gt;
&lt;li&gt;No audit trail&lt;/li&gt;
&lt;li&gt;Deployment anxiety 😰&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;Git-driven deployments&lt;/li&gt;
&lt;li&gt;Automatic drift correction&lt;/li&gt;
&lt;li&gt;Pull request reviews&lt;/li&gt;
&lt;li&gt;Confidence in production 😎&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure changes are now as simple as creating a pull request!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📝 &lt;a href="https://github.com/jamilshaikh07/homelab-gitops" rel="noopener noreferrer"&gt;Complete repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://crossplane.io/" rel="noopener noreferrer"&gt;Crossplane documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎯 &lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;Argo CD documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🏗️ &lt;a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/" rel="noopener noreferrer"&gt;App-of-Apps pattern&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What infrastructure will you GitOps next?&lt;/strong&gt; Drop a comment and let me know what you're planning to automate! 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more cloud-native and DevOps content! 🚀&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>argocd</category>
      <category>linux</category>
      <category>crossplane</category>
    </item>
  </channel>
</rss>
