<?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>DEV Community: Oleh</title>
    <description>The latest articles on DEV Community by Oleh (@redbul1ka).</description>
    <link>https://dev.to/redbul1ka</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042317%2Fd084369c-3727-4203-a557-5a59bfefa1a0.gif</url>
      <title>DEV Community: Oleh</title>
      <link>https://dev.to/redbul1ka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/redbul1ka"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Oleh</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:29:52 +0000</pubDate>
      <link>https://dev.to/redbul1ka/-18b5</link>
      <guid>https://dev.to/redbul1ka/-18b5</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db" class="crayons-story__hidden-navigation-link"&gt;From "kubectl apply" to a self-driving platform: a GitOps homelab, evolved&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/redbul1ka" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042317%2Fd084369c-3727-4203-a557-5a59bfefa1a0.gif" alt="redbul1ka profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/redbul1ka" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Oleh
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Oleh
                
              
              &lt;div id="story-author-preview-content-4207551" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/redbul1ka" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042317%2Fd084369c-3727-4203-a557-5a59bfefa1a0.gif" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Oleh&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db" id="article-link-4207551"&gt;
          From "kubectl apply" to a self-driving platform: a GitOps homelab, evolved
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gitops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gitops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/kubernetes"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;kubernetes&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/argocd"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;argocd&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>From "kubectl apply" to a self-driving platform: a GitOps homelab, evolved</title>
      <dc:creator>Oleh</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:09:56 +0000</pubDate>
      <link>https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db</link>
      <guid>https://dev.to/redbul1ka/from-kubectl-apply-to-a-self-driving-platform-a-gitops-homelab-evolved-28db</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Long read. This is the story of how a laptop cluster went from &lt;em&gt;"I threw some&lt;br&gt;
manifests at it"&lt;/em&gt; to a platform that ships and heals itself - and, more&lt;br&gt;
importantly, &lt;strong&gt;how the thinking changed at each step&lt;/strong&gt;. If you're weighing&lt;br&gt;
Argo CD, Istio, Vault or just "how do I structure a GitOps repo that won't&lt;br&gt;
rot", this is the map I wish I'd had.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I run a four-node &lt;strong&gt;kind&lt;/strong&gt; cluster on my desktop. Early on I made one rule: &lt;strong&gt;I&lt;br&gt;
don't touch the cluster. I touch git.&lt;/strong&gt; No &lt;code&gt;kubectl apply&lt;/code&gt; by hand, no "let me&lt;br&gt;
just patch this one thing on the side." If it isn't in a repo, it doesn't&lt;br&gt;
exist. Everything below is what that rule forced me to learn.&lt;/p&gt;


&lt;h2&gt;
  
  
  Phase 1 - the foundation: the app must not know about the cluster
&lt;/h2&gt;

&lt;p&gt;The first real design decision is the one everything else hangs off, so I'll&lt;br&gt;
state it plainly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The application knows nothing about the cluster. The cluster knows how to&lt;br&gt;
run the application.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Concretely: my two demo services (&lt;code&gt;front&lt;/code&gt;, &lt;code&gt;back&lt;/code&gt;) live in their own repos.&lt;br&gt;
Their CI builds an image (Kaniko, no Docker daemon), packages a Helm chart, and&lt;br&gt;
pushes both to the GitLab OCI registry. Then it does the only thing that&lt;br&gt;
touches the platform - it writes a single number, the new chart version, into a&lt;br&gt;
JSON file in the GitOps repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"back"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"chart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"backend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"chartVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.10"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An Argo CD &lt;strong&gt;ApplicationSet&lt;/strong&gt; watches those files and turns each one into an&lt;br&gt;
&lt;code&gt;Application&lt;/code&gt; that pulls the chart straight from the registry. The app repo&lt;br&gt;
never holds a kubeconfig, never runs &lt;code&gt;kubectl&lt;/code&gt;, never knows the gateway exists.&lt;br&gt;
It ships a chart version; the platform decides how that runs.&lt;/p&gt;

&lt;p&gt;The platform itself is the &lt;strong&gt;app-of-apps&lt;/strong&gt; pattern: one root &lt;code&gt;Application&lt;/code&gt;,&lt;br&gt;
applied once, that renders only control-plane objects - projects, the&lt;br&gt;
ApplicationSet, and one small &lt;code&gt;app.yaml&lt;/code&gt; per component. Each of those is its own&lt;br&gt;
&lt;code&gt;Application&lt;/code&gt; owning its own manifests. So the entire cluster is &lt;code&gt;kubectl apply&lt;br&gt;
-f root.yaml&lt;/code&gt;, once, and after that Argo manages everything, including Argo.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; this split is what lets a robot bump a version and a human&lt;br&gt;
never log in. It's also what keeps the app charts portable - they describe a&lt;br&gt;
workload and a Service, nothing environment-specific.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2 - maturing the GitOps logic: structure, ordering, and deleting things
&lt;/h2&gt;

&lt;p&gt;A working app-of-apps is step one. Making it &lt;em&gt;not rot&lt;/em&gt; is the real work, and it&lt;br&gt;
came in three moves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete the dead weight.&lt;/strong&gt; I used to run self-hosted GitLab &lt;em&gt;inside&lt;/em&gt; the&lt;br&gt;
cluster - its own Postgres, Redis, MinIO. For a homelab that's just mass. I&lt;br&gt;
ripped it out, moved CI to gitlab.com, and dropped the GitLab database and roles&lt;br&gt;
from Postgres. I also killed a custom image-updater the moment CI started&lt;br&gt;
writing the chart version itself. Half of GitOps maturity is realizing what you&lt;br&gt;
can &lt;em&gt;remove&lt;/em&gt; - every component you delete is one you never have to reconcile,&lt;br&gt;
secure, or debug at 3am.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Group by domain.&lt;/strong&gt; The flat &lt;code&gt;platform/*&lt;/code&gt; pile grew to ~14 components and got&lt;br&gt;
unreadable. I regrouped into &lt;code&gt;platform/{mesh,security,data,identity,ci,core}/&lt;/code&gt;,&lt;br&gt;
with &lt;code&gt;cluster/&lt;/code&gt; for Argo's own governance and &lt;code&gt;apps/&lt;/code&gt; for workloads. The quiet&lt;br&gt;
lesson here: Argo tracks Applications by &lt;strong&gt;name, not path&lt;/strong&gt;, so &lt;code&gt;git mv&lt;/code&gt; +&lt;br&gt;
fixing each &lt;code&gt;source.path&lt;/code&gt; + the root glob is a zero-downtime refactor - nothing&lt;br&gt;
gets recreated. (I froze the root app - &lt;code&gt;selfHeal: false, prune: false&lt;/code&gt; - during&lt;br&gt;
the move, so a bad glob couldn't prune half the cluster before I noticed. That&lt;br&gt;
two-line insurance is not optional.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add guardrails and ordering.&lt;/strong&gt; &lt;code&gt;AppProject&lt;/code&gt;s became allow-lists: which repos a&lt;br&gt;
component may pull from, which namespaces it may write to, which cluster-scoped&lt;br&gt;
kinds it may touch. Boring until the day a typo tries to land in &lt;code&gt;kube-system&lt;/code&gt;&lt;br&gt;
and the project simply says no. And Argo &lt;strong&gt;sync-waves&lt;/strong&gt; encode the ordering that&lt;br&gt;
actually matters - the CloudNativePG operator comes up in one wave, the Postgres&lt;br&gt;
&lt;code&gt;Cluster&lt;/code&gt; in the next, because the CRDs and webhooks have to exist before you&lt;br&gt;
can create a resource against them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 - the hardcore infra: mesh, state, and secrets that never touch git
&lt;/h2&gt;

&lt;p&gt;With the logic clean, the platform got real capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingress ? Gateway API on Istio.&lt;/strong&gt; I replaced ingress-nginx with the Gateway&lt;br&gt;
API served by Istio: one &lt;code&gt;Gateway&lt;/code&gt; on port 80, one &lt;code&gt;HTTPRoute&lt;/code&gt; per host. The&lt;br&gt;
interesting part was kind - no LoadBalancer, so the gateway pod has to bind&lt;br&gt;
&lt;code&gt;hostPort: 80&lt;/code&gt; on the control-plane node, exactly how nginx did. Getting there&lt;br&gt;
meant teaching the istio-generated Deployment a nodeSelector, a control-plane&lt;br&gt;
toleration, the host port, and a &lt;code&gt;Recreate&lt;/code&gt; strategy (hostPort + RollingUpdate&lt;br&gt;
deadlocks - the new pod can't bind a port the old one still holds). Slightly&lt;br&gt;
outside pure GitOps because Istio owns that Deployment, but it works and Istio&lt;br&gt;
leaves the extra fields alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State: CloudNativePG.&lt;/strong&gt; Postgres runs as a CNPG &lt;code&gt;Cluster&lt;/code&gt;, hosting the app&lt;br&gt;
database. It's the backbone that makes stateful demos possible and survives pod&lt;br&gt;
restarts - the boring, reliable core the rest leans on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets, evolved.&lt;/strong&gt; This is the part I'm most happy with, because it shows a&lt;br&gt;
progression rather than a single tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;v1:&lt;/em&gt; &lt;strong&gt;SealedSecrets&lt;/strong&gt; - encrypted secrets committed to git, decrypted only
in-cluster. Great for bootstrap material.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;v2:&lt;/em&gt; &lt;strong&gt;Vault + External Secrets Operator.&lt;/strong&gt; The database password lives in
Vault (source of truth). ESO authenticates to Vault with the &lt;strong&gt;Kubernetes auth
method&lt;/strong&gt; - it presents a ServiceAccount token, Vault validates it via
TokenReview and maps it to a policy that may read exactly one path, and ESO
materializes a Kubernetes &lt;code&gt;Secret&lt;/code&gt; the app consumes. &lt;strong&gt;The password never
touches git and is never typed into a manifest.&lt;/strong&gt; The wiring is declarative
(a &lt;code&gt;SecretStore&lt;/code&gt; + an &lt;code&gt;ExternalSecret&lt;/code&gt;); the value is fetched at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make all of this &lt;em&gt;tangible&lt;/em&gt; I rebuilt the demo app into a small guestbook:&lt;br&gt;
nginx frontend ? &lt;code&gt;/api&lt;/code&gt; ? a Flask backend that runs its own SQL migrations on&lt;br&gt;
boot and reads/writes CloudNativePG, with its DB credentials delivered through&lt;br&gt;
that Vault ? ESO chain. Every layer of the platform is visible in one page -&lt;br&gt;
&lt;code&gt;DATABASE: connected&lt;/code&gt;, a visit counter, and messages persisted in Postgres:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw232yxo4b92sh39s1slo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw232yxo4b92sh39s1slo.png" alt=" " width="800" height="476"&gt;&lt;/a&gt; &lt;strong&gt;Screenshot - the guestbook:&lt;/strong&gt; &lt;code&gt;DATABASE: connected&lt;/code&gt;, a live visit counter, and a message wall persisted in CloudNativePG. &lt;em&gt;(add &lt;code&gt;01-app-guestbook.png&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That "DATABASE: connected" pill is the whole platform working end to end - CI&lt;br&gt;
built the image with Kaniko, Argo deployed it, ESO pulled the password out of&lt;br&gt;
Vault, and CloudNativePG is holding the rows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1wa6mara5slg50fqa4q7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1wa6mara5slg50fqa4q7.png" alt=" " width="800" height="840"&gt;&lt;/a&gt; &lt;strong&gt;Screenshot - Argo CD:&lt;/strong&gt; all 19 applications &lt;code&gt;Synced&lt;/code&gt; + &lt;code&gt;Healthy&lt;/code&gt;. &lt;em&gt;(add &lt;code&gt;02-argo-apps.png&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  War stories - because troubleshooting is half the job
&lt;/h2&gt;

&lt;p&gt;Pretty YAML is easy. The signal that you can run this stuff is what happens when&lt;br&gt;
it breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The day Argo tried to delete itself.&lt;/strong&gt; Argo manages Argo here, so when I&lt;br&gt;
bumped the &lt;code&gt;argo-cd&lt;/code&gt; chart, it tried to reconcile its own workloads. The new&lt;br&gt;
chart changed the pod &lt;code&gt;selectorLabels&lt;/code&gt; - and &lt;code&gt;Deployment.spec.selector&lt;/code&gt; is&lt;br&gt;
&lt;strong&gt;immutable&lt;/strong&gt;. Kubernetes rejected the update on every Argo component, the&lt;br&gt;
Services quietly adopted the new selectors while the Pods kept the old labels,&lt;br&gt;
and a Service whose selector matches nothing has &lt;strong&gt;zero endpoints&lt;/strong&gt;.&lt;br&gt;
&lt;code&gt;argocd-server&lt;/code&gt;: no endpoints, 503 - and the tool that's supposed to fix Argo&lt;br&gt;
couldn't reach its own repo-server to render itself. I hand-recreated the&lt;br&gt;
control plane (delete + recreate so the immutable selectors could change) while&lt;br&gt;
the UI threw 503s at me. Lesson, permanently: &lt;strong&gt;bumping the Argo chart across a&lt;br&gt;
selector change means recreating its workloads by hand.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The quieter ones.&lt;/strong&gt; Istiod injects a webhook caBundle at runtime, so Argo saw&lt;br&gt;
perpetual drift until I added &lt;code&gt;ignoreDifferences&lt;/code&gt;. ESO's CRDs are big enough to&lt;br&gt;
blow past the 256 KiB last-applied-configuration annotation limit, so its&lt;br&gt;
controller crash-looped until I switched that Application to&lt;br&gt;
&lt;code&gt;ServerSideApply=true&lt;/code&gt;. Each one obvious in hindsight, each one only learnable&lt;br&gt;
by getting bitten.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reality check - the trade-offs I chose on purpose
&lt;/h2&gt;

&lt;p&gt;Senior isn't "it's all perfect," it's "I know exactly where the sharp edges&lt;br&gt;
are":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vault auto-unseals itself&lt;/strong&gt; via a CronJob and a single hand-created unseal
key (&lt;code&gt;shares=1&lt;/code&gt;). That's a &lt;em&gt;lab&lt;/em&gt; decision - it trades real security for the
cluster coming back on its own after a restart. In production the unseal key
never sits next to the thing it unseals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudNativePG runs a single instance.&lt;/strong&gt; State survives pod restarts, but
there's no HA - one instance, one backup story I haven't built yet. Fine for a
lab, not for anything with an SLA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The gateway hostPort patch lives slightly outside GitOps&lt;/strong&gt; because Istio
owns that Deployment. It's declarative &lt;em&gt;enough&lt;/em&gt; and stable, but it's the one
seam where I traded purity for "works in kind."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DB creds are static-in-Vault, not dynamic yet.&lt;/strong&gt; The database engine is
wired; dynamic per-connection users are the next step, and they need a shared
owner role so rotated users still see the schema. Deliberately deferred, not
forgotten.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where it landed
&lt;/h2&gt;

&lt;p&gt;Nineteen applications, all green. CI ships images, Argo ships the platform, the&lt;br&gt;
platform ships the apps, Vault hands out the secrets, and I run none of it - I&lt;br&gt;
run git. The whole thing rebuilds from &lt;code&gt;kubectl apply -f root.yaml&lt;/code&gt; and a&lt;br&gt;
handful of &lt;code&gt;git push&lt;/code&gt;es.&lt;/p&gt;

&lt;p&gt;It broke, I fixed it, and now it's boring to operate. &lt;strong&gt;Boring is the trophy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want the guts - the app-of-apps root, the Vault?ESO wiring, the gateway&lt;br&gt;
hostPort hack? Say the word and I'll drop a follow-up on any one of them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gitops</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>argocd</category>
    </item>
  </channel>
</rss>
