<?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: varun varde</title>
    <description>The latest articles on DEV Community by varun varde (@varunvarde).</description>
    <link>https://dev.to/varunvarde</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%2F3761696%2Fecce1536-897c-45d3-ba4f-11e7d0b344ed.jpg</url>
      <title>DEV Community: varun varde</title>
      <link>https://dev.to/varunvarde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/varunvarde"/>
    <language>en</language>
    <item>
      <title>From GitHub Actions to ArgoCD: A Complete GitOps Migration Guide</title>
      <dc:creator>varun varde</dc:creator>
      <pubDate>Tue, 07 Apr 2026 13:33:40 +0000</pubDate>
      <link>https://dev.to/varunvarde/from-github-actions-to-argocd-a-complete-gitops-migration-guide-4kd4</link>
      <guid>https://dev.to/varunvarde/from-github-actions-to-argocd-a-complete-gitops-migration-guide-4kd4</guid>
      <description>&lt;p&gt;I've done this migration three times now. Each time, I learn something new about what NOT to do. This guide is the documentation I wish I'd had the first time: the complete migration from a multi-stage GitHub Actions deployment pipeline to a fully declarative GitOps workflow with ArgoCD including the parts every other tutorial glosses over.&lt;/p&gt;

&lt;p&gt;What you'll build: Production ready ArgoCD installation, ApplicationSet for multi cluster management, Image Updater for automated rollouts, RBAC configuration for multi-team environments, Rollback procedures that work under pressure.&lt;/p&gt;

&lt;p&gt;Prerequisites: Kubernetes cluster (1.24+), kubectl, Helm 3, existing GitHub Actions CI pipeline. Estimated time: 4-6 hours for learning setup (30 days for production migration).&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Assessment — Mapping Your Existing GitHub Actions Pipeline
&lt;/h2&gt;

&lt;p&gt;Every successful migration begins with clarity. Before introducing GitOps, the existing CI/CD topology must be dissected—methodically, not casually.&lt;/p&gt;

&lt;p&gt;Start by inventorying workflows&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;# .github/workflows/deploy.yml&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;Deploy Service&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build -t myapp .&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubectl apply -f k8s/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pipeline reveals implicit assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deployment is tightly coupled with CI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kubernetes manifests are applied imperatively&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No state reconciliation exists&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Map each component&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Build Step&lt;/td&gt;
&lt;td&gt;Artifact creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy Step&lt;/td&gt;
&lt;td&gt;Cluster mutation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets&lt;/td&gt;
&lt;td&gt;Runtime configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The goal is not replication but transformation. GitOps demands declarative intent, not procedural execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: ArgoCD Installation (Production-Grade Helm Values)
&lt;/h2&gt;

&lt;p&gt;A default installation is rarely sufficient. Production demands rigor secure ingress, RBAC, and resource constraints.&lt;/p&gt;

&lt;p&gt;Install ArgoCD using Helm&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add argo https://argoproj.github.io/argo-helm
helm &lt;span class="nb"&gt;install &lt;/span&gt;argocd argo/argo-cd &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A hardened values.yaml might resemble&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;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;

  &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;argocd.example.com&lt;/span&gt;

&lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server.insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="na"&gt;cm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://argocd.example.com&lt;/span&gt;

&lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;controller&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="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;500m&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512Mi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security is not ornamental. It is foundational. Misconfigured access at this stage invites future instability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Structuring Your GitOps Repository
&lt;/h2&gt;

&lt;p&gt;Git becomes the single source of truth. Structure, therefore, becomes paramount.&lt;/p&gt;

&lt;p&gt;A canonical layout&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gitops-repo/
├── apps/
│   ├── payments/
│   ├── auth/
├── environments/
│   ├── dev/
│   ├── staging/
│   └── prod/
└── infrastructure/
    ├── ingress/
    └── monitoring/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each directory encapsulates intent.&lt;/p&gt;

&lt;p&gt;Example application manifest&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;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;payments-service&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/org/gitops-repo&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/payments&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD&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;payments&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="na"&gt;selfHeal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Declarative. Predictable. Auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 4: ApplicationSet Configuration for Multi-Team
&lt;/h2&gt;

&lt;p&gt;At scale, manually defining applications becomes untenable. ApplicationSet introduces dynamic orchestration.&lt;/p&gt;

&lt;p&gt;A Git generator example&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;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;ApplicationSet&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;team-apps&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;generators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;git&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/org/gitops-repo&lt;/span&gt;
        &lt;span class="na"&gt;revision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
        &lt;span class="na"&gt;directories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&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="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{path.basename}}'&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;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&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/org/gitops-repo&lt;/span&gt;
        &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{path}}'&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{path.basename}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Teams simply add a folder. The system reacts autonomously.&lt;/p&gt;

&lt;p&gt;This is where GitOps begins to feel almost sentient responsive, adaptive, yet deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 5: Migrating Your First Service (with Rollback Plan)
&lt;/h2&gt;

&lt;p&gt;Start small. Select a non-critical service. Minimize risk while maximizing insight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Remove Deploy Step from CI&lt;/strong&gt;&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;# Keep build, remove kubectl apply&lt;/span&gt;
- run: docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Push Kubernetes Manifests to GitOps Repo&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/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;Deployment&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;myapp&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;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Let ArgoCD Sync&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ArgoCD reconciles desired state with actual state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollback Strategy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rollback becomes trivial&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git revert &amp;lt;commit-hash&amp;gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual intervention. No frantic patching. Just version control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 6: Image Updater for CI Integration
&lt;/h2&gt;

&lt;p&gt;CI still builds images. But deployment is decoupled.&lt;/p&gt;

&lt;p&gt;ArgoCD Image Updater bridges the gap&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;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;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/image-list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp=repo/myapp&lt;/span&gt;
    &lt;span class="na"&gt;argocd-image-updater.argoproj.io/update-strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipeline pushes image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push repo/myapp:1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Image Updater modifies Git&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;repo/myapp:1.2.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArgoCD syncs automatically.&lt;/p&gt;

&lt;p&gt;Elegant. Autonomous.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 7: RBAC and Multi-Tenant Configuration
&lt;/h2&gt;

&lt;p&gt;As teams proliferate, governance becomes essential.&lt;/p&gt;

&lt;p&gt;Define roles&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;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;ConfigMap&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;argocd-rbac-cm&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;policy.csv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;p, role:dev, applications, get, */*, allow&lt;/span&gt;
    &lt;span class="s"&gt;p, role:dev, applications, sync, */*, allow&lt;/span&gt;
    &lt;span class="s"&gt;g, team-dev, role:dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Namespace isolation&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&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;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;team-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each team operates within boundaries autonomous yet controlled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 8: Observability for Your GitOps Pipelines
&lt;/h2&gt;

&lt;p&gt;Visibility transforms guesswork into certainty.&lt;/p&gt;

&lt;p&gt;Enable metrics&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;controller&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scrape with Prometheus&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;argocd'&lt;/span&gt;
  &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;argocd-metrics:8082'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key metrics&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sync status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reconciliation duration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Drift detection&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logs provide narrative&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs deployment/argocd-application-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without observability, GitOps becomes a black box. With it, a glass box.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Things No Tutorial Mentions (Learned the Hard Way)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Git Becomes Your Bottleneck&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Frequent commits can overwhelm repositories. Optimize branching strategies. Avoid unnecessary churn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Drift Happens More Than Expected&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Manual changes in clusters still occur. ArgoCD corrects them—but not always instantly. Expect transient inconsistencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Secrets Are a Persistent Challenge&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Plain YAML is insufficient. Integrate tools like sealed secrets or external secret managers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. ApplicationSet Can Spiral into Complexity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dynamic generation is powerful—but dangerous. Poor templates create cascading misconfigurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Human Behavior Is the Hardest Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Engineers accustomed to imperative deployments resist change. Training is not optional. It is essential.&lt;/p&gt;

&lt;p&gt;Migrating to ArgoCD is not merely a tooling shift. It is a philosophical transition—from imperative control to declarative intent.&lt;/p&gt;

&lt;p&gt;The rewards are substantial. Stability. Traceability. Confidence.&lt;/p&gt;

&lt;p&gt;But the journey demands discipline. And a willingness to relinquish old habits in favor of something far more resilient.&lt;/p&gt;

</description>
      <category>gitops</category>
      <category>argocd</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Zero Downtime Cloud Migration: The 6-Phase Playbook</title>
      <dc:creator>varun varde</dc:creator>
      <pubDate>Thu, 26 Mar 2026 08:27:40 +0000</pubDate>
      <link>https://dev.to/varunvarde/zero-downtime-cloud-migration-the-6-phase-playbook-13p8</link>
      <guid>https://dev.to/varunvarde/zero-downtime-cloud-migration-the-6-phase-playbook-13p8</guid>
      <description>&lt;p&gt;Phase 1 is never 'lift and shift.' Here's the framework that keeps production stable throughout the entire move.&lt;/p&gt;

&lt;p&gt;After leading migrations for organizations ranging from 200 to 4,000 engineers, I've distilled it into 6 phases that keep production alive and teams sane.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Discovery &amp;amp; Dependency Mapping&lt;/strong&gt;&lt;br&gt;
Before touching a single VM, audit everything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Inventory all services: apps, databases, middleware, integrations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Map inter service dependencies including the undocumented ones (ask the engineers who've been there longest)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tag everything by criticality, data sensitivity, migration complexity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify the "spiders in the web" services everything else depends on&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools: AWS Application Discovery Service, Cloudamize, or a structured spreadsheet + interviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Cloud Foundation &amp;amp; Landing Zone&lt;/strong&gt;&lt;br&gt;
Build the platform before you migrate anything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Set up your VPC architecture (hub spoke or flat decide now)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement IAM roles, SCPs, and guardrails&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy centralized logging, monitoring, and alerting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Establish your IaC standard (Terraform modules, Pulumi stacks — standardize before day one)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set cost budgets and alerts BEFORE anything runs&lt;br&gt;
Never skip this. Teams that migrate first and "sort out governance later" always regret it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Pilot Migration (Noncritical workloads)&lt;/strong&gt;&lt;br&gt;
Start small. Build confidence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Choose a non-critical, low traffic service&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run full migration lifecycle: move → validate → monitor → optimize&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document everything. This becomes your runbook for every subsequent migration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify gaps in tooling and process while the stakes are low&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Wave Based Migration&lt;/strong&gt;&lt;br&gt;
Organize workloads into migration waves by complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Wave 1: Stateless apps (easiest)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wave 2: Stateful apps with managed DB alternatives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wave 3: Complex integrations and legacy services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wave 4 (optional): Services that require refactoring before migration&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run each wave over 2–4-week sprints. Include a 2-week stabilization window after each wave before proceeding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5: Cutover &amp;amp; Traffic Management&lt;/strong&gt;&lt;br&gt;
Zero-downtime means dual running, not big-bang.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use feature flags and DNS-based traffic shifting (Route 53 weighted routing or equivalent)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement a circuit breaker to instantly roll back traffic if error rates spike&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep on prem running in parallel for 30–60 days post cutover&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't decommission anything until you've run at least one full billing cycle on cloud&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Phase 6: Optimize &amp;amp; Decommission&lt;/strong&gt;&lt;br&gt;
The migration isn't over when the apps are running.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Right-size instances based on real usage data (wait at least 2 weeks)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement autoscaling where you were running fixed capacity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up FinOps dashboards cloud spend will be surprising at first&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decommission on prem systematically, not in a rush&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run a migration retrospective with every team involved&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Rule I Never Break&lt;/strong&gt;&lt;br&gt;
No service goes to production without observability in place (logs, metrics, traces), a documented runbook, an on-call rotation assigned, and a tested rollback procedure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save for your next migration kickoff&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cloudskills</category>
      <category>aws</category>
      <category>webpack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Terraform Modules That Actually Scale: Patterns from 20 Years</title>
      <dc:creator>varun varde</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:47:03 +0000</pubDate>
      <link>https://dev.to/varunvarde/terraform-modules-that-actually-scale-patterns-from-20-years-35j7</link>
      <guid>https://dev.to/varunvarde/terraform-modules-that-actually-scale-patterns-from-20-years-35j7</guid>
      <description>&lt;p&gt;Provisioning was once an artisanal craft defined by shell scripts and fragile golden images. We've since pivoted to the declarative, idempotent paradigm of Terraform. But scaling isn't just about resource count it’s about managing the cognitive load of massive environments. At some point, the question stops being how do I write HCL? and becomes how do I stop this complexity from collapsing under its own weight?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Monolith Trap: Why Large State Files Kill Velocity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Mega State is the ultimate architectural debt. Dumping your VPC, EKS cluster, and RDS instances into one root module creates a catastrophic blast radius. One syntax error in a security group shouldn't lock the state file for your entire production database. Scaling demands decoupling. Infrastructure components must be treated as independent lifecycles. Networking is slow and steady app pods are ephemeral. Isolate them to minimize plan times and risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strict Typing and Input Orthogonality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ditch type = any. It’s lazy and dangerous. High utility modules require strict object validation to fail fast. If the calling code sends a malformed schema, the plan should die immediately not thirty minutes into a deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"cluster_config"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS spec"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;version&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;node_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;instance_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;capacity_type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"1.28"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1.29"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1.30"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"K8s version restricted by organizational compliance."&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compositional Strategy: Favoring Layering over Nesting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nesting modules inside modules leads to Prop Drilling a nightmare where a variable is passed through five layers of code just to reach a single resource. It's brittle.&lt;/p&gt;

&lt;p&gt;Layering is the professional choice. Keep modules flat and use a Composition Root to stitch outputs to inputs. This keeps the logic readable and the dependencies explicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross Account Orchestration via Provider Aliasing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enterprise scale means multiple regions and hundreds of AWS accounts. Never hardcode provider blocks inside a module; it makes them non portable. Use Provider Aliases. This allows you to instantiate the same module across different regions within a single run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instance in US-East&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"s3_east"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/s3"&lt;/span&gt;
  &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;us-east-1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Instance in US-West&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"s3_west"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/s3"&lt;/span&gt;
  &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;us-west-2&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Immutable Versioning and Private Registries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Local file paths are for hobbyists. In production, referencing a local path means every save on your laptop could theoretically break a CI/CD pipeline. Scaling requires immutable versioning. Use Git tags or a Private Registry. It’s the only way to ensure Production stays on v1.2.0 while Dev experiments with v2.0.0 beta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Validation: Terratest and OPA Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you aren't testing, you're guessing. For core modules, Terratest is the standard. It spins up real resources, pings them to ensure they work, and tears them down. Couple this with Open Policy Agent (OPA) to programmatically block anyone from creating an unencrypted volume or a public S3 bucket before the code even leaves the PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 2 Operations: Refactoring with moved Blocks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Refactoring used to mean terraform state mv commands that risked corrupting the remote backend. Now, we have the moved block. It allows you to move resources into modules without triggering a "destroy and recreate" cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;moved&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;legacy_app&lt;/span&gt;
  &lt;span class="nx"&gt;to&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how you turn a tangled mess of HCL into a robust ecosystem. It’s about predictability, isolation, and strict interfaces.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
