<?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: John  Ajera</title>
    <description>The latest articles on DEV Community by John  Ajera (@jajera).</description>
    <link>https://dev.to/jajera</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%2F2461160%2Fefda9ea8-49f8-4d07-b358-445b8e7d5a20.png</url>
      <title>DEV Community: John  Ajera</title>
      <link>https://dev.to/jajera</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jajera"/>
    <language>en</language>
    <item>
      <title>Argo CD and AWS CodeConnections: The Upside, the Redeploy Pain, and How I Fixed It</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 10:46:16 +0000</pubDate>
      <link>https://dev.to/jajera/argo-cd-and-aws-codeconnections-the-upside-the-redeploy-pain-and-how-i-fixed-it-2k1m</link>
      <guid>https://dev.to/jajera/argo-cd-and-aws-codeconnections-the-upside-the-redeploy-pain-and-how-i-fixed-it-2k1m</guid>
      <description>&lt;h2&gt;
  
  
  Argo CD and AWS CodeConnections: The Upside, the Redeploy Pain, and How I Fixed It
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;Argo CD&lt;/a&gt; on &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;Amazon EKS&lt;/a&gt; using the managed &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;Argo CD capability&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections&lt;/a&gt; for Git. CodeConnections has been a clear win for day-to-day operations. Then I had to &lt;strong&gt;recreate&lt;/strong&gt; the connection (new resource, new identity in the URL). Every Application went to &lt;strong&gt;Sync: Unknown&lt;/strong&gt; until I updated URLs in &lt;strong&gt;two&lt;/strong&gt; places—Git &lt;strong&gt;and&lt;/strong&gt; the live cluster—and fixed &lt;strong&gt;ApplicationSets&lt;/strong&gt; so they stopped writing the old URL back. This article leads with &lt;strong&gt;why I still choose CodeConnections&lt;/strong&gt;, then &lt;strong&gt;what breaks on redeploy&lt;/strong&gt;, then &lt;strong&gt;what I did&lt;/strong&gt; when it inevitably happened, in that order.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Why CodeConnections is worth it for Argo CD on EKS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No SSH keys or personal tokens in the cluster.&lt;/strong&gt; Argo pulls Git using IAM: the capability role is allowed to use the connection (&lt;code&gt;UseConnection&lt;/code&gt;, &lt;code&gt;GetConnection&lt;/code&gt;). You are not copying PATs into Secrets or rotating leaked keys because someone printed &lt;code&gt;kubectl get secret&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One connection, many repos.&lt;/strong&gt; The HTTPS URL includes your account, region, a &lt;strong&gt;connection UUID&lt;/strong&gt;, and then &lt;code&gt;owner/repo&lt;/code&gt;. Same connection, different path segment for each repository. Setup details and Terraform patterns are in &lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fits how enterprises already govern access.&lt;/strong&gt; Connections are AWS resources; approval and auditing live next to the rest of your cloud controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That does not mean zero tradeoffs.&lt;/strong&gt; Managed Argo CD often applies &lt;strong&gt;one&lt;/strong&gt; Git credential broadly (for example every &lt;code&gt;github.com&lt;/code&gt; fetch through Kustomize can inherit it). If that bites you, vendoring or URL strategy fixes it—see &lt;a href="https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6"&gt;Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)&lt;/a&gt;. The tradeoff this article focuses on is different: &lt;strong&gt;replacing&lt;/strong&gt; the connection.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. What actually hurts when you update or redeploy the connection
&lt;/h3&gt;

&lt;p&gt;The Git URL Argo uses is not abstract. It embeds a &lt;strong&gt;connection UUID&lt;/strong&gt; in the path. &lt;strong&gt;A new connection is a new UUID.&lt;/strong&gt; Anything that still points at the old path keeps asking AWS to authorize the wrong resource, so repo fetch fails and sync never reconciles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point one — Git only is not enough.&lt;/strong&gt; Your GitOps repo is the source of truth, but Kubernetes already has &lt;code&gt;Application&lt;/code&gt; and &lt;code&gt;ApplicationSet&lt;/code&gt; objects applied &lt;strong&gt;yesterday&lt;/strong&gt;. Their &lt;code&gt;spec.source.repoURL&lt;/code&gt; (and generator URLs on sets) stay on the old string until something updates them. Pushing Git does not retroactively patch those CRs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point two — ApplicationSets fight you.&lt;/strong&gt; Many sets declare &lt;code&gt;repoURL&lt;/code&gt; twice: on the &lt;strong&gt;git&lt;/strong&gt; generator and again on the &lt;strong&gt;template&lt;/strong&gt;. If you patch child Applications but leave the set on the old URL, the controller reconciles and &lt;strong&gt;puts the old URL back&lt;/strong&gt; on the apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point three — Terraform layout.&lt;/strong&gt; If CodeConnections and the EKS cluster share one tangled module and state, &lt;strong&gt;recreating&lt;/strong&gt; the connection can feel like you are planning half the platform when you only wanted a new Git pipe. I now prefer the connection (and its IAM attachment) in something &lt;strong&gt;standalone&lt;/strong&gt;, with outputs the cluster stack consumes—so the next rotation is a smaller blast radius.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this is not.&lt;/strong&gt; If Argo shows &lt;strong&gt;forbidden&lt;/strong&gt; listing some API group (for example heavy CRD surfaces from controllers like ACK), that is usually &lt;strong&gt;cluster RBAC / EKS access policies&lt;/strong&gt; for the Argo identity, not the CodeConnections URL. Fix that on its own; do not confuse it with a UUID swap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not mass-delete Applications&lt;/strong&gt; to “fix” a bad URL. Workloads may still be fine; you risk prune tearing down real resources. Fix the URLs.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; against the cluster, with permission to read and patch &lt;code&gt;applications&lt;/code&gt; and &lt;code&gt;applicationsets&lt;/code&gt; in the Argo CD namespace (below I use &lt;code&gt;argocd&lt;/code&gt;, the usual default)&lt;/li&gt;
&lt;li&gt;Rights to &lt;strong&gt;commit and push&lt;/strong&gt; every Git repo that hardcodes the CodeConnections URL&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;new&lt;/strong&gt; clone URL or at least the new UUID from the AWS Console or Terraform output&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. If it happens to you: what worked for me
&lt;/h3&gt;

&lt;p&gt;These steps assume you already know the &lt;strong&gt;old&lt;/strong&gt; and &lt;strong&gt;new&lt;/strong&gt; connection UUID (search your shell history, Terraform state, or an old Application YAML). The host pattern is documented in the CodeConnections and EKS guides linked at the end.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step A — Fix Git first, everywhere
&lt;/h4&gt;

&lt;p&gt;Search across &lt;strong&gt;all&lt;/strong&gt; repos that participate in GitOps—not only the “main” infra repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rg &lt;span class="s1"&gt;'codeconnections\.'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.yaml'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.yml'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.tf'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.md'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update bootstrap &lt;code&gt;Application&lt;/code&gt; manifests, every &lt;code&gt;ApplicationSet&lt;/code&gt; &lt;strong&gt;generator&lt;/strong&gt; and &lt;strong&gt;template&lt;/strong&gt; &lt;code&gt;repoURL&lt;/code&gt;, any child &lt;code&gt;Application&lt;/code&gt; checked in with a literal URL, and docs or scripts that build repository secrets. Commit and push.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step B — Patch Applications in the cluster
&lt;/h4&gt;

&lt;p&gt;Still in a broken state, the cluster cannot always sync from Git, so you patch live objects once. Set shell variables to your real values (namespace if not &lt;code&gt;argocd&lt;/code&gt;, old UUID, new UUID):&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="nv"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;argocd
&lt;span class="nv"&gt;OLD_UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
&lt;span class="nv"&gt;NEW_UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List apps whose single &lt;code&gt;spec.source.repoURL&lt;/code&gt; still contains the old UUID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get applications &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; u &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'.items[] | select(.spec.source.repoURL // "" | contains($u)) | .metadata.name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each name, the safest approach is to take the existing URL from the object and swap the UUID in the shell, then merge-patch:&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="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-app
&lt;span class="nv"&gt;oldurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get application &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.spec.source.repoURL}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;newurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;oldurl&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$NEW_UUID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
kubectl patch application &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; merge &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;spec&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;repoURL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$newurl&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;strong&gt;&lt;code&gt;spec.sources&lt;/code&gt;&lt;/strong&gt; (multi-source), repeat the idea per entry that points at CodeConnections.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step C — Patch ApplicationSets (do not skip this)
&lt;/h4&gt;

&lt;p&gt;Confirm the old UUID still appears on &lt;strong&gt;spec&lt;/strong&gt; (ignore &lt;strong&gt;status&lt;/strong&gt; for a moment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get applicationsets &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | rg &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To replace the UUID everywhere under each set’s JSON (review before you run this in production—I exported one set first and eyeballed it):&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="k"&gt;for &lt;/span&gt;name &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;kubectl get applicationsets &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; u &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'.items[] | select(.. | strings? | contains($u)) | .metadata.name'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;kubectl get applicationset &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | &lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;--arg&lt;/span&gt; o &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'del(.status) | walk(if type == "string" then gsub($o; $n) else . end)'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    kubectl replace &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;walk&lt;/code&gt; replaces &lt;strong&gt;every&lt;/strong&gt; occurrence of the old UUID string in that JSON. Pick a UUID substring unique to the connection so you do not hit unrelated fields.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step D — Refresh and converge
&lt;/h4&gt;

&lt;p&gt;Hard refresh apps in the UI or CLI, then let GitOps catch up. If Git still cannot be fetched until &lt;strong&gt;one&lt;/strong&gt; app is fixed, patch your &lt;strong&gt;bootstrap&lt;/strong&gt; Application (the one that applies the rest of the tree) to the new URL first, then repeat the rest—classic chicken-and-egg.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Troubleshooting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Application stuck deleting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch application my-app &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"metadata":{"finalizers":null}}'&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  I deleted an app and it came back
&lt;/h4&gt;

&lt;p&gt;An &lt;strong&gt;ApplicationSet&lt;/strong&gt; owns it (&lt;code&gt;ownerReferences&lt;/code&gt;). Fix the set and Git; deleting the app alone will not stick.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;Amazon EKS: Argo CD capability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/application-specification/" rel="noopener noreferrer"&gt;Argo CD – Application spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/" rel="noopener noreferrer"&gt;Argo CD – ApplicationSet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6"&gt;Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>codeconnections</category>
      <category>eks</category>
      <category>gitops</category>
    </item>
    <item>
      <title>Host a Static Site on EC2 with Terraform (VPC, Optional ALB)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 08:54:20 +0000</pubDate>
      <link>https://dev.to/jajera/host-a-static-site-on-ec2-with-terraform-vpc-optional-alb-1ba9</link>
      <guid>https://dev.to/jajera/host-a-static-site-on-ec2-with-terraform-vpc-optional-alb-1ba9</guid>
      <description>&lt;h2&gt;
  
  
  Host a Static Site on EC2 with Terraform (VPC, Optional ALB, Session Manager)
&lt;/h2&gt;

&lt;p&gt;For static sites, &lt;strong&gt;S3 + CloudFront&lt;/strong&gt; is usually the better default. This post points at a small Terraform demo and pulls a few excerpts from &lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;iam.tf&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;user_data.tftpl&lt;/code&gt;&lt;/strong&gt;. Full layout: &lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo" rel="noopener noreferrer"&gt;tf-aws-ec2-static-demo&lt;/a&gt; (local path &lt;code&gt;~/workspace/jdevto/tf-aws-ec2-static-demo&lt;/code&gt; if you keep it beside this blog repo). &lt;strong&gt;S3 + CloudFront&lt;/strong&gt; with Terraform: &lt;a href="https://github.com/jdevto/blog/blob/main/articles/art0018.md" rel="noopener noreferrer"&gt;art0018&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;The demo provisions a VPC, &lt;strong&gt;nginx&lt;/strong&gt; on &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt; (AZ + private IP from &lt;strong&gt;IMDSv2&lt;/strong&gt;), and &lt;strong&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;use_alb=false&lt;/code&gt; (default):&lt;/strong&gt; one instance in one &lt;strong&gt;public&lt;/strong&gt; subnet; clients hit &lt;strong&gt;:80&lt;/strong&gt; on the instance public IP (CIDR from &lt;strong&gt;&lt;code&gt;allowed_http_cidr&lt;/code&gt;&lt;/strong&gt;). &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;:&lt;/strong&gt; &lt;strong&gt;internet ALB&lt;/strong&gt; across &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt; ≥ 2&lt;/strong&gt; public subnets; &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt;&lt;/strong&gt; instances in &lt;strong&gt;private&lt;/strong&gt; subnets (one per AZ), &lt;strong&gt;NAT&lt;/strong&gt; for egress, no instance public IP; instances register to one target group with &lt;strong&gt;HTTP /&lt;/strong&gt; health check expecting &lt;strong&gt;200&lt;/strong&gt;; instance SG allows &lt;strong&gt;:80&lt;/strong&gt; from the &lt;strong&gt;ALB&lt;/strong&gt; SG and from the &lt;strong&gt;VPC CIDR&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;enable_ssm=true&lt;/code&gt; (default):&lt;/strong&gt; &lt;strong&gt;Session Manager&lt;/strong&gt; with &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt;—no SSH in the template.&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;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_count&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;instance_count&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"az_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="c1"&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;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&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;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&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;"az_count must be between 1 and 6, and at least 2 when use_alb is true (ALB requirement)."&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;h3&gt;
  
  
  Why EC2?
&lt;/h3&gt;

&lt;p&gt;Learning stack (Terraform + VPC + optional ELB), policy that prefers VPC-hosted sites, temporary lift-and-shift, or a box that might grow non-static behavior later. None of that makes EC2 the default for a &lt;strong&gt;pure&lt;/strong&gt; static site.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC
&lt;/h3&gt;

&lt;p&gt;Every instance is in a &lt;strong&gt;VPC&lt;/strong&gt; and a &lt;strong&gt;subnet&lt;/strong&gt; (&lt;a href="https://aws.amazon.com/ec2/faqs/#ec2-classic" rel="noopener noreferrer"&gt;EC2-Classic&lt;/a&gt; is gone for new accounts). The demo creates its own VPC—public subnets always; &lt;strong&gt;private subnets + NAT&lt;/strong&gt; when &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use_alb = false
  +----------+   :80 (public IP)   +----------------+
  | Clients  | ------------------&amp;gt; | EC2 (nginx)    |
  +----------+                     +----------------+

use_alb = true
  +----------+   :80   +-----+   :80   +----------------+
  | Clients  | ------&amp;gt; | ALB | ------&amp;gt; | EC2 × az_count |
  +----------+         +-----+         | (private)      |
                                      +----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NAT&lt;/strong&gt; is billed when the ALB path is on. Repeated &lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt; to the ALB can show different &lt;strong&gt;AZ / private IP&lt;/strong&gt; in &lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt; as backends rotate. &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt; fills those fields via &lt;strong&gt;IMDSv2&lt;/strong&gt; after &lt;strong&gt;nginx&lt;/strong&gt; is up:&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="nv"&gt;IMDS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://169.254.169.254/latest/api/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token-ttl-seconds: 21600"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;PRIVATE_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$IMDS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://169.254.169.254/latest/meta-data/local-ipv4"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;AZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$IMDS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://169.254.169.254/latest/meta-data/placement/availability-zone"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt; — placement and bootstrap:&lt;/strong&gt;&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;

  &lt;span class="nx"&gt;subnet_id&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&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="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_security_group_ids&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_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;user_data&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;templatefile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${path.module}/user_data.tftpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;enable_ssm&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;enable_ssm&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;user_data_replace_on_change&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;iam_instance_profile&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;enable_ssm&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_instance_profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssm&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="nx"&gt;name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="c1"&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;&lt;code&gt;main.tf&lt;/code&gt; — instance ingress (ALB on) and target group health check:&lt;/strong&gt;&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;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;content&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;"HTTP from ALB security group (forwarded client traffic)"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&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_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;content&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;"HTTP from VPC for ALB health checks and internal probes"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&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_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_block&lt;/span&gt;&lt;span class="p"&gt;]&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_target_group"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="nx"&gt;port&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
  &lt;span class="nx"&gt;protocol_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP1"&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;

  &lt;span class="nx"&gt;health_check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
    &lt;span class="nx"&gt;port&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"traffic-port"&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
    &lt;span class="nx"&gt;matcher&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt;
    &lt;span class="nx"&gt;interval&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nx"&gt;healthy_threshold&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;unhealthy_threshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;h3&gt;
  
  
  Session Manager
&lt;/h3&gt;

&lt;p&gt;Agent + &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt;; &lt;strong&gt;&lt;code&gt;user_data.tftpl&lt;/code&gt;&lt;/strong&gt; via &lt;strong&gt;&lt;code&gt;templatefile&lt;/code&gt;&lt;/strong&gt; so &lt;strong&gt;&lt;code&gt;#!/bin/bash&lt;/code&gt;&lt;/strong&gt; is at column 0 (indented &lt;strong&gt;&lt;code&gt;heredoc&lt;/code&gt;&lt;/strong&gt; in Terraform often breaks the shebang). With &lt;strong&gt;&lt;code&gt;enable_ssm&lt;/code&gt;&lt;/strong&gt;, the template pulls the &lt;strong&gt;SSM Agent RPM from S3&lt;/strong&gt; and starts the service. Use the AMI’s &lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt;—do not &lt;strong&gt;&lt;code&gt;dnf install curl&lt;/code&gt;&lt;/strong&gt; on AL2023 (conflicts with &lt;strong&gt;&lt;code&gt;curl-minimal&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;set -e&lt;/code&gt;&lt;/strong&gt; aborts before &lt;strong&gt;nginx&lt;/strong&gt;). Egress: &lt;strong&gt;NAT&lt;/strong&gt; or &lt;strong&gt;SSM VPC endpoints&lt;/strong&gt;. Docs: &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/agent-install-al2.html" rel="noopener noreferrer"&gt;agent install&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-status-and-restart.html" rel="noopener noreferrer"&gt;status&lt;/a&gt;. Your principal needs &lt;strong&gt;&lt;code&gt;ssm:StartSession&lt;/code&gt;&lt;/strong&gt;; &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" rel="noopener noreferrer"&gt;Session Manager plugin&lt;/a&gt; for CLI. After apply, wait for &lt;strong&gt;Online&lt;/strong&gt; in Fleet Manager; with &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;&lt;/strong&gt;, use &lt;strong&gt;&lt;code&gt;instance_ids&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;ssm_start_session_command&lt;/code&gt;&lt;/strong&gt; (first instance). &lt;strong&gt;&lt;code&gt;%{ if enable_ssm ~}&lt;/code&gt;&lt;/strong&gt; … &lt;strong&gt;&lt;code&gt;%{ endif ~}&lt;/code&gt;&lt;/strong&gt; wraps the SSM block in the template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam.tf&lt;/code&gt;:&lt;/strong&gt;&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"ssm_core"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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;enable_ssm&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssm&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="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"&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;&lt;code&gt;user_data.tftpl&lt;/code&gt; (SSM path):&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;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Column-0 shebang required: indented Terraform heredocs break #!/bin/bash and cloud-init may skip the script.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eux&lt;/span&gt;
&lt;span class="c"&gt;# Do not `dnf install curl` here: AL2023 ships curl-minimal; installing full curl conflicts and aborts the whole script under set -e.&lt;/span&gt;
&lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;x86_64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  aarch64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_arm64/amazon-ssm-agent.rpm"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unsupported arch for SSM agent RPM"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/amazon-ssm-agent.rpm &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SSM_RPM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; /tmp/amazon-ssm-agent.rpm
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/amazon-ssm-agent.rpm
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;amazon-ssm-agent
systemctl restart amazon-ssm-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  robots.txt
&lt;/h3&gt;

&lt;p&gt;Crawler hint file—not security. The demo writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: *
Allow: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;user_data.tftpl&lt;/code&gt;:&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="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/usr/share/nginx/html/robots.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;ROBOTS&lt;/span&gt;&lt;span class="sh"&gt;'
User-agent: *
Allow: /
&lt;/span&gt;&lt;span class="no"&gt;ROBOTS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/crawling-indexing/robots/intro" rel="noopener noreferrer"&gt;Google: robots.txt&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jdevto/tf-aws-ec2-static-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;tf-aws-ec2-static-demo
terraform init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ALB (needs ≥2 AZs; default &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt;&lt;/strong&gt; is 3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"use_alb=true"&lt;/span&gt;
&lt;span class="c"&gt;# terraform apply -var="use_alb=true" -var="az_count=2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait &lt;strong&gt;1–2 minutes&lt;/strong&gt; after first boot for &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;user_data_replace_on_change = true&lt;/code&gt;&lt;/strong&gt; replaces instances when the template changes. Variables: repo &lt;strong&gt;&lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;&lt;/strong&gt;.&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;"use_alb"&lt;/span&gt; &lt;span class="p"&gt;{&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;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"allowed_http_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&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;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"enable_ssm"&lt;/span&gt; &lt;span class="p"&gt;{&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;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform output verify_commands
&lt;span class="c"&gt;# use_alb=false:&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; instance_public_ip&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/robots.txt"&lt;/span&gt;
&lt;span class="c"&gt;# use_alb=true (no instance public IP):&lt;/span&gt;
&lt;span class="c"&gt;# curl -sS "$(terraform output -raw website_url_alb)/robots.txt"&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; ssm_start_session_command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Timeout / connection refused&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;:&lt;/strong&gt; use ALB URL, not instance IP. &lt;strong&gt;&lt;code&gt;false&lt;/code&gt;:&lt;/strong&gt; SG, &lt;strong&gt;&lt;code&gt;allowed_http_cidr&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;instance_public_ip&lt;/code&gt;&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALB unhealthy / &lt;strong&gt;502&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;SG &lt;strong&gt;:80&lt;/strong&gt; from ALB + VPC; &lt;strong&gt;&lt;code&gt;/&lt;/code&gt;&lt;/strong&gt; returns &lt;strong&gt;200&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;cloud-init-output.log&lt;/code&gt;:&lt;/strong&gt; failed &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt; (e.g. &lt;strong&gt;&lt;code&gt;dnf install curl&lt;/code&gt;&lt;/strong&gt; vs &lt;strong&gt;&lt;code&gt;curl-minimal&lt;/code&gt;&lt;/strong&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply limits&lt;/td&gt;
&lt;td&gt;Other region/account or limit increase.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM offline / denied&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;enable_ssm&lt;/code&gt;&lt;/strong&gt;, agent time, &lt;strong&gt;&lt;code&gt;ssm:StartSession&lt;/code&gt;&lt;/strong&gt;, outbound to SSM (NAT or endpoints).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo" rel="noopener noreferrer"&gt;tf-aws-ec2-static-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" rel="noopener noreferrer"&gt;Terraform AWS provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html" rel="noopener noreferrer"&gt;Amazon VPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;Application Load Balancer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html" rel="noopener noreferrer"&gt;Session Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" rel="noopener noreferrer"&gt;Session Manager plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>ec2</category>
      <category>vpc</category>
      <category>ssm</category>
    </item>
    <item>
      <title>Using GoAccess to View nginx Logs</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 08:54:19 +0000</pubDate>
      <link>https://dev.to/jajera/using-goaccess-to-view-nginx-logs-1d1c</link>
      <guid>https://dev.to/jajera/using-goaccess-to-view-nginx-logs-1d1c</guid>
      <description>&lt;h2&gt;
  
  
  Using GoAccess to View nginx Logs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt; is a fast terminal and HTML log analyzer for nginx access logs (requests/sec, status codes, top URLs, referrers, user agents). This guide covers installation, optional S3 download, and typical run modes.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;GoAccess&lt;/strong&gt; (package manager, source, or &lt;a href="https://github.com/jdevto/cli-tools" rel="noopener noreferrer"&gt;jdevto/cli-tools&lt;/a&gt; script)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; sync logs from &lt;strong&gt;S3&lt;/strong&gt; when they are not on the machine you analyze from&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;TUI&lt;/strong&gt;, &lt;strong&gt;HTML report&lt;/strong&gt;, &lt;strong&gt;live tail&lt;/strong&gt;, or &lt;strong&gt;compressed&lt;/strong&gt; input; use &lt;code&gt;COMBINED&lt;/code&gt; or &lt;code&gt;COMMON&lt;/code&gt; to match nginx &lt;code&gt;log_format&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip §4 if logs are already on disk.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Readable &lt;strong&gt;nginx access logs&lt;/strong&gt; (usually &lt;strong&gt;combined&lt;/strong&gt; or &lt;strong&gt;common&lt;/strong&gt; format)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sudo&lt;/strong&gt; for distro package installs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 (optional):&lt;/strong&gt; AWS CLI v2 and &lt;code&gt;s3:GetObject&lt;/code&gt; / &lt;code&gt;s3:ListBucket&lt;/code&gt; for the prefix you use&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install GoAccess
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Fedora / RHEL / CentOS Stream
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Debian / Ubuntu
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Newer or custom build:&lt;/strong&gt; &lt;a href="https://goaccess.io/download" rel="noopener noreferrer"&gt;official build instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  jdevto/cli-tools installer (optional)
&lt;/h4&gt;

&lt;p&gt;Builds from the &lt;a href="https://tar.goaccess.io" rel="noopener noreferrer"&gt;official tarball&lt;/a&gt;; details and env vars in &lt;a href="https://github.com/jdevto/cli-tools/blob/main/docs/install_goaccess.md" rel="noopener noreferrer"&gt;install_goaccess.md&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; uninstall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Optional: Download nginx Logs from S3
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Single file
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log ~/nginx-logs/access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key is &lt;code&gt;.gz&lt;/code&gt; only:&lt;/strong&gt; download, decompress, or stream without extracting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log.gz ~/nginx-logs/access.log.gz
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ~/nginx-logs/access.log.gz
&lt;span class="c"&gt;# or: zcat ~/nginx-logs/access.log.gz | goaccess - --log-format=COMBINED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Prefix (many files)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://my-bucket/nginx-logs/ ~/nginx-logs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace bucket and prefix. If you only have &lt;strong&gt;&lt;code&gt;*.gz&lt;/code&gt;&lt;/strong&gt;, after sync use &lt;code&gt;zcat ~/nginx-logs/*.gz &amp;gt; ~/nginx-logs/merged-access.log&lt;/code&gt; (order across files may not be chronological) or decompress then merge plain &lt;code&gt;*.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Merge plain logs (optional)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/nginx-logs/&lt;span class="k"&gt;*&lt;/span&gt;.log &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/nginx-logs/merged-access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Run GoAccess
&lt;/h3&gt;

&lt;p&gt;Match &lt;strong&gt;&lt;code&gt;--log-format&lt;/code&gt;&lt;/strong&gt; to nginx (&lt;code&gt;COMBINED&lt;/code&gt; is the usual default).&lt;/p&gt;

&lt;h4&gt;
  
  
  Terminal UI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  HTML report
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt;
xdg-open report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Live log
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common log format
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMMON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom &lt;code&gt;log_format&lt;/code&gt;: map fields with GoAccess &lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;custom format&lt;/a&gt; tokens.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compressed on disk
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcat /var/log/nginx/access.log.&lt;span class="k"&gt;*&lt;/span&gt;.gz | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://YOUR_BUCKET/YOUR_PREFIX/ ~/nginx-logs/   &lt;span class="c"&gt;# optional&lt;/span&gt;
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xdg-open report.html
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Parsed 0 lines / wrong numbers:&lt;/strong&gt; Log line shape ≠ &lt;code&gt;COMBINED&lt;/code&gt;/&lt;code&gt;COMMON&lt;/code&gt;. Compare &lt;code&gt;head -1&lt;/code&gt; to nginx &lt;code&gt;log_format&lt;/code&gt; and adjust &lt;code&gt;--log-format&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied on log:&lt;/strong&gt; &lt;code&gt;sudo goaccess ...&lt;/code&gt;, fix group on log files, or copy the log to your home directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Access Denied:&lt;/strong&gt; IAM on bucket/prefix; check &lt;code&gt;AWS_PROFILE&lt;/code&gt; / role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live tail idle:&lt;/strong&gt; Confirm nginx &lt;code&gt;access_log&lt;/code&gt; path and read permissions on the active file.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;GoAccess man (log formats)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/docs/http/ngx_http_log_module.html" rel="noopener noreferrer"&gt;nginx log module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html" rel="noopener noreferrer"&gt;AWS CLI s3 cp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>goaccess</category>
      <category>nginx</category>
      <category>logs</category>
      <category>aws</category>
    </item>
    <item>
      <title>Using GoAccess to View nginx Logs</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 22 Mar 2026 23:22:42 +0000</pubDate>
      <link>https://dev.to/jajera/using-goaccess-to-view-nginx-logs-1491</link>
      <guid>https://dev.to/jajera/using-goaccess-to-view-nginx-logs-1491</guid>
      <description>&lt;h2&gt;
  
  
  Using GoAccess to View nginx Logs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt; is a fast terminal and HTML log analyzer for nginx access logs (requests/sec, status codes, top URLs, referrers, user agents). This guide covers installation, optional S3 download, and typical run modes.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;GoAccess&lt;/strong&gt; (package manager, source, or &lt;a href="https://github.com/jdevto/cli-tools" rel="noopener noreferrer"&gt;jdevto/cli-tools&lt;/a&gt; script)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; sync logs from &lt;strong&gt;S3&lt;/strong&gt; when they are not on the machine you analyze from&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;TUI&lt;/strong&gt;, &lt;strong&gt;HTML report&lt;/strong&gt;, &lt;strong&gt;live tail&lt;/strong&gt;, or &lt;strong&gt;compressed&lt;/strong&gt; input; use &lt;code&gt;COMBINED&lt;/code&gt; or &lt;code&gt;COMMON&lt;/code&gt; to match nginx &lt;code&gt;log_format&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip §4 if logs are already on disk.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Readable &lt;strong&gt;nginx access logs&lt;/strong&gt; (usually &lt;strong&gt;combined&lt;/strong&gt; or &lt;strong&gt;common&lt;/strong&gt; format)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sudo&lt;/strong&gt; for distro package installs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 (optional):&lt;/strong&gt; AWS CLI v2 and &lt;code&gt;s3:GetObject&lt;/code&gt; / &lt;code&gt;s3:ListBucket&lt;/code&gt; for the prefix you use&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install GoAccess
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Fedora / RHEL / CentOS Stream
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Debian / Ubuntu
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Newer or custom build:&lt;/strong&gt; &lt;a href="https://goaccess.io/download" rel="noopener noreferrer"&gt;official build instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  jdevto/cli-tools installer (optional)
&lt;/h4&gt;

&lt;p&gt;Builds from the &lt;a href="https://tar.goaccess.io" rel="noopener noreferrer"&gt;official tarball&lt;/a&gt;; details and env vars in &lt;a href="https://github.com/jdevto/cli-tools/blob/main/docs/install_goaccess.md" rel="noopener noreferrer"&gt;install_goaccess.md&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; uninstall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Optional: Download nginx Logs from S3
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Single file
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log ~/nginx-logs/access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key is &lt;code&gt;.gz&lt;/code&gt; only:&lt;/strong&gt; download, decompress, or stream without extracting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log.gz ~/nginx-logs/access.log.gz
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ~/nginx-logs/access.log.gz
&lt;span class="c"&gt;# or: zcat ~/nginx-logs/access.log.gz | goaccess - --log-format=COMBINED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Prefix (many files)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://my-bucket/nginx-logs/ ~/nginx-logs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace bucket and prefix. If you only have &lt;strong&gt;&lt;code&gt;*.gz&lt;/code&gt;&lt;/strong&gt;, after sync use &lt;code&gt;zcat ~/nginx-logs/*.gz &amp;gt; ~/nginx-logs/merged-access.log&lt;/code&gt; (order across files may not be chronological) or decompress then merge plain &lt;code&gt;*.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Merge plain logs (optional)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/nginx-logs/&lt;span class="k"&gt;*&lt;/span&gt;.log &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/nginx-logs/merged-access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Run GoAccess
&lt;/h3&gt;

&lt;p&gt;Match &lt;strong&gt;&lt;code&gt;--log-format&lt;/code&gt;&lt;/strong&gt; to nginx (&lt;code&gt;COMBINED&lt;/code&gt; is the usual default).&lt;/p&gt;

&lt;h4&gt;
  
  
  Terminal UI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  HTML report
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt;
xdg-open report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Live log
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common log format
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMMON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom &lt;code&gt;log_format&lt;/code&gt;: map fields with GoAccess &lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;custom format&lt;/a&gt; tokens.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compressed on disk
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcat /var/log/nginx/access.log.&lt;span class="k"&gt;*&lt;/span&gt;.gz | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://YOUR_BUCKET/YOUR_PREFIX/ ~/nginx-logs/   &lt;span class="c"&gt;# optional&lt;/span&gt;
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xdg-open report.html
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Parsed 0 lines / wrong numbers:&lt;/strong&gt; Log line shape ≠ &lt;code&gt;COMBINED&lt;/code&gt;/&lt;code&gt;COMMON&lt;/code&gt;. Compare &lt;code&gt;head -1&lt;/code&gt; to nginx &lt;code&gt;log_format&lt;/code&gt; and adjust &lt;code&gt;--log-format&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied on log:&lt;/strong&gt; &lt;code&gt;sudo goaccess ...&lt;/code&gt;, fix group on log files, or copy the log to your home directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Access Denied:&lt;/strong&gt; IAM on bucket/prefix; check &lt;code&gt;AWS_PROFILE&lt;/code&gt; / role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live tail idle:&lt;/strong&gt; Confirm nginx &lt;code&gt;access_log&lt;/code&gt; path and read permissions on the active file.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;GoAccess man (log formats)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/docs/http/ngx_http_log_module.html" rel="noopener noreferrer"&gt;nginx log module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html" rel="noopener noreferrer"&gt;AWS CLI s3 cp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>goaccess</category>
      <category>nginx</category>
      <category>logs</category>
      <category>aws</category>
    </item>
    <item>
      <title>Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:50:40 +0000</pubDate>
      <link>https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6</link>
      <guid>https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6</guid>
      <description>&lt;h2&gt;
  
  
  Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)
&lt;/h2&gt;

&lt;p&gt;You switched to managed Argo CD (e.g. EKS Capabilities) and use AWS CodeConnections for Git. Your Applications that pull from &lt;strong&gt;public&lt;/strong&gt; GitHub remotes in Kustomize suddenly fail with errors like "Password authentication is not supported" or "Authentication failed". The same kustomization worked with self-managed Argo CD. This article explains why CodeConnections causes that and how &lt;strong&gt;vendoring&lt;/strong&gt; those remote bases into your repo fixes it.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explains why Kustomize remote resources (e.g. &lt;code&gt;github.com/open-policy-agent/gatekeeper-library/...&lt;/code&gt;) break when Argo CD uses CodeConnections for Git&lt;/li&gt;
&lt;li&gt;Shows that the credential helper is applied to every &lt;code&gt;github.com&lt;/code&gt; URL, including public repos, and GitHub rejects those credentials&lt;/li&gt;
&lt;li&gt;Describes when to vendor and a minimal how-to: download remote files at a pinned ref, add them to your repo, and point your kustomization at local files instead of remote URLs&lt;/li&gt;
&lt;li&gt;Suggests keeping a README in the vendored directory so future upgrades are a repeatable re-download and commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it breaks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With managed Argo CD + CodeConnections, one credential is used for all GitHub access. When Kustomize runs &lt;code&gt;git fetch&lt;/code&gt; for a remote base, Argo CD passes that same credential. Public repos don't need auth—but they receive it anyway, and GitHub rejects the format (e.g. "Password authentication is not supported").&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Managed Argo CD (e.g. &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;EKS Argo CD capability&lt;/a&gt;) with &lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections&lt;/a&gt; used for Git (see &lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt; for setup)&lt;/li&gt;
&lt;li&gt;At least one Argo CD Application whose source uses Kustomize with &lt;strong&gt;remote&lt;/strong&gt; &lt;code&gt;resources&lt;/code&gt; or &lt;code&gt;bases&lt;/code&gt; pointing at GitHub (e.g. &lt;code&gt;github.com/org/repo/path?ref=master&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Why remote bases break with CodeConnections
&lt;/h3&gt;

&lt;p&gt;When Argo CD syncs an Application, the repo-server runs &lt;code&gt;kustomize build&lt;/code&gt; over your repo. If your &lt;code&gt;kustomization.yaml&lt;/code&gt; lists remote resources like:&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;github.com/open-policy-agent/gatekeeper-library/library/general/httpsonly?ref=master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kustomize invokes &lt;code&gt;git fetch&lt;/code&gt; for that URL. Argo CD configures a Git credential helper so that any git operation gets credentials. With CodeConnections, that helper returns the &lt;strong&gt;same&lt;/strong&gt; credential (the one for your connected repos) for &lt;strong&gt;every&lt;/strong&gt; &lt;code&gt;github.com&lt;/code&gt; request—including requests to public repos such as &lt;code&gt;open-policy-agent/gatekeeper-library&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Git sends those credentials to GitHub. GitHub then responds with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal: Authentication failed for 'https://github.com/open-policy-agent/gatekeeper-library/'
remote: Invalid username or token. Password authentication is not supported for Git operations.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the build fails even though the remote repo is public and would work with no credentials. With self-managed Argo CD you might have used SSH for your app repo; Kustomize’s HTTPS fetches to public GitHub then didn’t use that credential and succeeded. With CodeConnections, one HTTPS credential is used everywhere, and it gets sent to public URLs where it’s invalid.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. When to vendor
&lt;/h3&gt;

&lt;p&gt;Vendor remote bases when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You use &lt;strong&gt;managed Argo CD with CodeConnections&lt;/strong&gt; and your Kustomize app references &lt;strong&gt;public&lt;/strong&gt; GitHub remotes (e.g. gatekeeper-library, shared bases from other orgs).&lt;/li&gt;
&lt;li&gt;You see authentication or "Password authentication is not supported" errors during manifest generation for those remotes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also vendor remotes you don’t control so that upgrades are explicit and reproducible (pin to a commit, re-download when you want to upgrade).&lt;/p&gt;




&lt;h3&gt;
  
  
  5. How to vendor
&lt;/h3&gt;

&lt;p&gt;Vendoring means copying the remote manifest files into your repo and pointing Kustomize at local files instead of remote URLs.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose a ref&lt;/strong&gt; — Use a commit SHA or tag from the upstream repo (e.g. &lt;code&gt;master&lt;/code&gt; or a specific SHA) so you can reproduce the same content later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download the remote files&lt;/strong&gt; — Each remote resource is usually a directory containing one or more YAML files (e.g. &lt;code&gt;template.yaml&lt;/code&gt;). Download those files using the raw GitHub URL pattern:
&lt;code&gt;https://raw.githubusercontent.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;/&amp;lt;ref&amp;gt;/&amp;lt;path&amp;gt;/&amp;lt;file&amp;gt;.yaml&lt;/code&gt;
Save them under a directory in your repo (e.g. &lt;code&gt;infrastructure/my-app/vendored/&lt;/code&gt;) with clear names (e.g. &lt;code&gt;httpsonly.yaml&lt;/code&gt;, &lt;code&gt;requiredlabels.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update your kustomization&lt;/strong&gt; — Replace remote &lt;code&gt;resources&lt;/code&gt; entries with the local file names:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before (remote – fails with CodeConnections)&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="s"&gt;github.com/open-policy-agent/gatekeeper-library/library/general/httpsonly?ref=master&lt;/span&gt;

&lt;span class="c1"&gt;# After (vendored)&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="s"&gt;httpsonly.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Document the source and upgrade process&lt;/strong&gt; — Add a README in the vendored directory that lists:

&lt;ul&gt;
&lt;li&gt;The upstream repo and the ref (commit SHA) you used&lt;/li&gt;
&lt;li&gt;The mapping from each local file to its upstream path&lt;/li&gt;
&lt;li&gt;How to upgrade: re-download from a new ref, overwrite the files, run &lt;code&gt;kustomize build .&lt;/code&gt; to verify, commit, and update the README with the new ref.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Optionally, add a small script or one-liner (e.g. a shell loop with &lt;code&gt;curl&lt;/code&gt;) that downloads all vendored files given a ref, so upgrades are a single command plus commit.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Summary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Managed Argo CD + CodeConnections sends one GitHub credential to every git URL. Kustomize remote bases to public GitHub repos get that credential; GitHub rejects it and the build fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Vendor those remote bases: download the manifest files at a pinned ref, put them in your repo, and point &lt;code&gt;kustomization.yaml&lt;/code&gt; at the local files. No remote fetch at build time, so no credential is used for those resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrades:&lt;/strong&gt; Re-download from a new ref, overwrite the vendored files, verify with &lt;code&gt;kustomize build .&lt;/code&gt;, commit, and update the README with the new ref.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Checklist when vendoring:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a ref (e.g. latest &lt;code&gt;master&lt;/code&gt; or a commit SHA) from the upstream repo.&lt;/li&gt;
&lt;li&gt;Download each remote file via &lt;code&gt;https://raw.githubusercontent.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;/&amp;lt;ref&amp;gt;/&amp;lt;path&amp;gt;/&amp;lt;file&amp;gt;.yaml&lt;/code&gt; into a directory in your repo.&lt;/li&gt;
&lt;li&gt;Replace remote &lt;code&gt;resources&lt;/code&gt; in &lt;code&gt;kustomization.yaml&lt;/code&gt; with the local file names.&lt;/li&gt;
&lt;li&gt;Add a README in that directory with source ref, file mapping, and upgrade steps (and optionally a download script).&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Manifest generation still fails after vendoring
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;kustomization.yaml&lt;/code&gt; lists only &lt;strong&gt;local&lt;/strong&gt; file names (e.g. &lt;code&gt;httpsonly.yaml&lt;/code&gt;), not &lt;code&gt;github.com/...&lt;/code&gt; URLs.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;kustomize build .&lt;/code&gt; from the directory that contains the kustomization and vendored files; fix any path or resource errors before relying on Argo CD.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Upstream added or removed a file
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Re-download from the ref you want (e.g. new commit on &lt;code&gt;master&lt;/code&gt;). Add or remove the corresponding local file and update the &lt;code&gt;resources&lt;/code&gt; list and README.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/kustomize/" rel="noopener noreferrer"&gt;Argo CD – Kustomize&lt;/a&gt; (including private remote bases)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt; (CodeConnections setup)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/open-policy-agent/gatekeeper-library" rel="noopener noreferrer"&gt;gatekeeper-library&lt;/a&gt; (example upstream that’s often used as a Kustomize remote)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>kubernetes</category>
      <category>kustomize</category>
      <category>vendoring</category>
    </item>
    <item>
      <title>Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 14 Mar 2026 07:31:29 +0000</pubDate>
      <link>https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-kck</link>
      <guid>https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-kck</guid>
      <description>&lt;h2&gt;
  
  
  Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform
&lt;/h2&gt;

&lt;p&gt;Argo CD on EKS Capabilities needs to pull from your Git repos. Instead of storing personal access tokens or SSH keys in the cluster, use &lt;strong&gt;AWS CodeConnections&lt;/strong&gt;: one connection authorizes Argo CD to access GitHub via IAM. This guide gives the minimal Terraform (connection + IAM policy on the Argo CD role) and the one-time Console steps to move the connection from Pending to Available. One connection can serve many repos; you only change the owner/repo in the URL.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a CodeStar connection (GitHub) and grants the Argo CD capability role &lt;code&gt;codeconnections:UseConnection&lt;/code&gt; and &lt;code&gt;codeconnections:GetConnection&lt;/code&gt; so Argo CD can pull from Git without credentials in the cluster&lt;/li&gt;
&lt;li&gt;Walks through the one-time Console step to complete the connection (Pending → Available), including using the &lt;strong&gt;GitHub App&lt;/strong&gt; (e.g. AWS Connector for GitHub) for org repos&lt;/li&gt;
&lt;li&gt;Shows the CodeConnections repo URL format for Argo CD Applications (connection ID is the UUID only, not the ARN)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why CodeConnections?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No PAT or SSH key in Kubernetes; the Argo CD role gets IAM permission to use the connection&lt;/li&gt;
&lt;li&gt;One connection = multiple GitHub repos (same connection ID, different owner/repo in the URL)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;EKS cluster with the &lt;strong&gt;Argo CD capability&lt;/strong&gt; enabled (Identity Center configured)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;name&lt;/strong&gt; of the Argo CD capability IAM role (e.g. from your EKS module: &lt;code&gt;cluster_name-argocd-capability-role&lt;/code&gt;, or from AWS Console → IAM → Roles)&lt;/li&gt;
&lt;li&gt;AWS Console access to complete the connection (GitHub OAuth)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Terraform: connection and IAM
&lt;/h3&gt;

&lt;p&gt;Add the following to your Terraform. Replace &lt;code&gt;argocd_capability_role_name&lt;/code&gt; with the actual role name (the role EKS created for the Argo CD capability).&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;"argocd_capability_role_name"&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;"Name of the IAM role used by the Argo CD EKS Capability"&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;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_codestarconnections_connection"&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt;
  &lt;span class="nx"&gt;provider_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"codeconnections:UseConnection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"codeconnections:GetConnection"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&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_codestarconnections_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"argocd-codeconnections"&lt;/span&gt;
  &lt;span class="nx"&gt;role&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;argocd_capability_role_name&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argocd_codeconnections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;terraform apply&lt;/code&gt;, the connection exists in &lt;strong&gt;Pending&lt;/strong&gt; state. Terraform cannot complete it; you do that once in the Console.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. After apply: complete the connection (Pending → Available)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://console.aws.amazon.com/" rel="noopener noreferrer"&gt;AWS Console&lt;/a&gt; in the same region as your Terraform.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Developer Tools&lt;/strong&gt; → &lt;strong&gt;Connections&lt;/strong&gt; (or search &lt;strong&gt;Connections&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Find the connection (e.g. &lt;code&gt;github&lt;/code&gt;). Status will be &lt;strong&gt;Pending&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the connection name → &lt;strong&gt;Update pending connection&lt;/strong&gt; (or &lt;strong&gt;Connect to GitHub&lt;/strong&gt;). You’ll land on the &lt;strong&gt;Connect to GitHub&lt;/strong&gt; page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;GitHub connection settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection name&lt;/strong&gt; — Confirm or set the name (e.g. &lt;code&gt;github&lt;/code&gt; to match your Terraform &lt;code&gt;name&lt;/code&gt;). This is how the connection appears in the Console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Installation (optional)&lt;/strong&gt; — Two choices:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leave blank&lt;/strong&gt; to connect as a &lt;strong&gt;GitHub user&lt;/strong&gt;. That user must have (at least read) access to the repos you use in Argo CD. Fine for personal or single-account repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a GitHub App&lt;/strong&gt; (recommended for org repos): e.g. choose &lt;strong&gt;AWS Connector for GitHub&lt;/strong&gt; via “Install a new app”, then select &lt;strong&gt;the org where your repos live&lt;/strong&gt; (e.g. &lt;code&gt;my-org&lt;/code&gt;) as the installation target.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click the orange &lt;strong&gt;Connect&lt;/strong&gt; button to start the GitHub authorization flow (or complete it after installing the app).&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you chose the GitHub App&lt;/strong&gt;, you’ll land on GitHub’s &lt;strong&gt;Install &amp;amp; Authorize AWS Connector for GitHub&lt;/strong&gt; page for that org. Choose &lt;strong&gt;Only select repositories&lt;/strong&gt;, click &lt;strong&gt;Select repositories&lt;/strong&gt;, and pick the repo(s) Argo CD will use (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;). Review the permissions (read/write access to code, pull requests, etc.), then click &lt;strong&gt;Install &amp;amp; Authorize&lt;/strong&gt;. You’ll be redirected to &lt;code&gt;https://redirect.codestar.aws/return&lt;/code&gt;. Back on the AWS &lt;strong&gt;Connect to GitHub&lt;/strong&gt; page, the &lt;strong&gt;App Installation&lt;/strong&gt; field will show your installation (e.g. an installation ID such as &lt;code&gt;123456789&lt;/code&gt;—yours will differ). Confirm &lt;strong&gt;Connection name&lt;/strong&gt; (e.g. &lt;code&gt;github&lt;/code&gt;), then click the orange &lt;strong&gt;Connect&lt;/strong&gt; button. The connection status should become &lt;strong&gt;Available&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you connected as a GitHub user&lt;/strong&gt;, complete the OAuth prompt, then return to the Console and confirm status is &lt;strong&gt;Available&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  5. Test the connection
&lt;/h3&gt;

&lt;p&gt;Once the connection is &lt;strong&gt;Available&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register the repo in Argo CD&lt;/strong&gt; (one-time). Create a repository Secret so the repo appears under Settings → Repositories. Use your real &lt;strong&gt;region&lt;/strong&gt;, &lt;strong&gt;account ID&lt;/strong&gt;, &lt;strong&gt;connection UUID&lt;/strong&gt;, and &lt;strong&gt;org/repo&lt;/strong&gt;—do not leave literal &lt;code&gt;OWNER/REPO&lt;/code&gt; in the URL or you’ll get “repository not found”. Example (replace the values if yours differ):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; Account &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONN_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;a1b2c3d4-e5f6-7890-abcd-ef1234567890   &lt;span class="c"&gt;# your connection UUID from Console or Terraform&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OWNER_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-org/my-repo                     &lt;span class="c"&gt;# your org and repo&lt;/span&gt;
   kubectl create secret generic argocd-repo-github &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://codeconnections.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/git-http/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONN_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER_REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt;
   kubectl label secret argocd-repo-github &lt;span class="nt"&gt;-n&lt;/span&gt; argocd argocd.argoproj.io/secret-type&lt;span class="o"&gt;=&lt;/span&gt;repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the secret already exists with the wrong URL, delete it first: &lt;code&gt;kubectl delete secret argocd-repo-github -n argocd&lt;/code&gt;, then run the &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;label&lt;/code&gt; commands above.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;Argo CD&lt;/strong&gt; → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Repositories&lt;/strong&gt;, click &lt;strong&gt;REFRESH LIST&lt;/strong&gt;. The repo should show with a green check (Successful). If it shows Failed, see the Troubleshooting section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an Application&lt;/strong&gt; that uses this repo: set &lt;code&gt;source.repoURL&lt;/code&gt; to the same CodeConnections URL and a path/targetRevision. Sync the application and confirm it syncs without errors.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  6. Using the repo URL in Argo CD
&lt;/h3&gt;

&lt;p&gt;CodeConnections URL format (replace placeholders):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://codeconnections.&amp;lt;region&amp;gt;.amazonaws.com/git-http/&amp;lt;account-id&amp;gt;/&amp;lt;region&amp;gt;/&amp;lt;connection-id&amp;gt;/OWNER/REPO.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;connection-id:&lt;/strong&gt; Must be the &lt;strong&gt;connection ID (UUID only)&lt;/strong&gt;, e.g. &lt;code&gt;9b6c3274-31da-4d1d-8955-e7d5cd9d15e2&lt;/code&gt;. Do &lt;strong&gt;not&lt;/strong&gt; put the full ARN in the URL path—that causes HTTP 400. In Terraform the AWS provider’s &lt;code&gt;aws_codestarconnections_connection.github.id&lt;/code&gt; is the ARN; use the UUID (the segment after the last &lt;code&gt;/&lt;/code&gt; in the ARN) or an output from your module that exposes the UUID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OWNER/REPO:&lt;/strong&gt; Your Git org and repo (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your Argo CD Application, set &lt;code&gt;source.repoURL&lt;/code&gt; to that URL. One connection works for many repos—same connection ID, change only owner/repo.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Summary: copy-paste
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Terraform:&lt;/strong&gt; Connection + IAM policy (role name from your EKS setup):&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_codestarconnections_connection"&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt;
  &lt;span class="nx"&gt;provider_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"argocd-codeconnections"&lt;/span&gt;
  &lt;span class="nx"&gt;role&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;argocd_capability_role_name&lt;/span&gt;  &lt;span class="c1"&gt;# your Argo CD capability role name&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&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="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"codeconnections:UseConnection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"codeconnections:GetConnection"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_codestarconnections_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
    &lt;span class="p"&gt;}]&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;Console:&lt;/strong&gt; Developer Tools → Connections → select connection → Update pending connection → on the Connect to GitHub page set/confirm Connection name, then either leave App Installation blank (user connection) or install &lt;strong&gt;AWS Connector for GitHub&lt;/strong&gt; and select the org that owns your repos (e.g. my-org) → click &lt;strong&gt;Connect&lt;/strong&gt; → complete GitHub auth → status &lt;strong&gt;Available&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Argo CD:&lt;/strong&gt; Use the CodeConnections URL with the connection ID and your &lt;code&gt;OWNER/REPO&lt;/code&gt; as &lt;code&gt;source.repoURL&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connection stays &lt;strong&gt;Pending&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Complete the connection in the AWS Console (authorize in GitHub or install the app). Check region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;HTTP 400&lt;/strong&gt; when Argo CD tests repo&lt;/td&gt;
&lt;td&gt;The repo URL path must use the &lt;strong&gt;connection ID (UUID only)&lt;/strong&gt;, not the full ARN. If your URL contains &lt;code&gt;arn:aws:codestar-connections:&lt;/code&gt; in the path, fix the URL to use only the UUID (e.g. from Terraform use a value that outputs the UUID, not &lt;code&gt;connection.id&lt;/code&gt; which is the ARN).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;repository not found&lt;/strong&gt; / &lt;strong&gt;ProviderResourceNotFoundException&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;The URL still has literal &lt;code&gt;OWNER/REPO&lt;/code&gt; instead of your real org and repo (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;). Delete the repo Secret and recreate it with the correct URL (see §5).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;authorization failed: Write access to repository not granted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The GitHub identity (user or app) used by the connection doesn’t have access to that repo. Install the &lt;strong&gt;GitHub App&lt;/strong&gt; (e.g. AWS Connector for GitHub) on the &lt;strong&gt;org that owns the repo&lt;/strong&gt; (e.g. my-org), or ensure the GitHub user that authorized has read access to the repo. Then re-authorize the connection if needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argo CD cannot fetch repo&lt;/td&gt;
&lt;td&gt;Argo CD role has &lt;code&gt;codeconnections:UseConnection&lt;/code&gt; and &lt;code&gt;codeconnections:GetConnection&lt;/code&gt; on the connection ARN; connection status &lt;strong&gt;Available&lt;/strong&gt;; &lt;code&gt;source.repoURL&lt;/code&gt; uses the UUID as connection-id and correct owner/repo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong region&lt;/td&gt;
&lt;td&gt;Connection is regional. Console and Terraform region must match.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  9. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argocd-configure-repositories.html" rel="noopener noreferrer"&gt;Configure repository access (Argo CD, CodeConnections)&lt;/a&gt; – AWS EKS User Guide&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/capabilities.html" rel="noopener noreferrer"&gt;EKS Capabilities&lt;/a&gt; – Argo CD, ACK, KRO&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>codeconnections</category>
      <category>eks</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Bitwarden Secrets Manager on EKS – Per-App Integration with Atlantis</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Mon, 02 Mar 2026 10:01:15 +0000</pubDate>
      <link>https://dev.to/jajera/bitwarden-secrets-manager-on-eks-per-app-integration-with-atlantis-5995</link>
      <guid>https://dev.to/jajera/bitwarden-secrets-manager-on-eks-per-app-integration-with-atlantis-5995</guid>
      <description>&lt;h2&gt;
  
  
  Bitwarden Secrets Manager on EKS – Per-App Integration with Atlantis
&lt;/h2&gt;

&lt;p&gt;Sync secrets from Bitwarden Secrets Manager into Kubernetes on EKS using the sm-operator, AWS Secrets Manager for the machine token, and the Secrets Store CSI Driver. This guide expands on the base integration with a &lt;strong&gt;per-app, per-namespace pattern&lt;/strong&gt; and uses &lt;strong&gt;Atlantis&lt;/strong&gt; as a concrete example. It covers Terraform, Kustomize overlays, Argo CD, sync waves, and troubleshooting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Use placeholder values for org IDs and secret IDs. Never commit real tokens. For production, follow least-privilege IAM and rotation practices.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrates Bitwarden Secrets Manager with EKS via sm-operator, AWS Secrets Manager, and Secrets Store CSI Driver&lt;/li&gt;
&lt;li&gt;Uses a &lt;strong&gt;per-app namespace pattern&lt;/strong&gt;: each app (e.g. Atlantis) gets its own SecretProviderClass, &lt;code&gt;bw-auth-token-sync&lt;/code&gt;, and BitwardenSecret in its own namespace&lt;/li&gt;
&lt;li&gt;Walks through Terraform (EKS + Pod Identity per app), manifests, Argo CD Applications, validation, and force-sync&lt;/li&gt;
&lt;li&gt;Uses Atlantis (Terraform PR automation) as a worked example with GitHub App credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why per-app namespaces?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolation: each app has its own Bitwarden machine token (scoped in AWS)&lt;/li&gt;
&lt;li&gt;Simplicity: the sm-operator creates the K8s Secret in the app’s namespace; no cross-namespace wiring&lt;/li&gt;
&lt;li&gt;Consistency: same pattern for every new app&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;EKS cluster with &lt;strong&gt;Secrets Store CSI Driver&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;Terraform-managed EKS (e.g. &lt;code&gt;terraform-aws-eks-basic&lt;/code&gt; with Secrets Manager support)&lt;/li&gt;
&lt;li&gt;Bitwarden Secrets Manager organization with Machine Account&lt;/li&gt;
&lt;li&gt;Per-app AWS secret path: &lt;code&gt;bitwarden/sm-operator/&amp;lt;app&amp;gt;/machine-token&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Architecture Overview
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bitwarden SM (Machine Account + App Secrets)
        │
        │ machine token (per app: bitwarden/sm-operator/&amp;lt;app&amp;gt;/machine-token)
        ▼
AWS Secrets Manager
        │
        │ Pod Identity + CSI (in app namespace, e.g. atlantis-1)
        ▼
SecretProviderClass + bw-auth-token-sync → creates bw-auth-token
        │
        ▼
BitwardenSecret (authToken: bw-auth-token)
        │
        │ sm-operator fetches via Bitwarden API
        ▼
Output K8s Secret (e.g. atlantis-1-vcs) in app namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flow (per app, e.g. atlantis-1):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Machine token stored in AWS Secrets Manager as &lt;code&gt;bitwarden/sm-operator/&amp;lt;app&amp;gt;/machine-token&lt;/code&gt; (JSON: &lt;code&gt;{"token":"&amp;lt;value&amp;gt;"}&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;SecretProviderClass + &lt;code&gt;bw-auth-token-sync&lt;/code&gt; Deployment in the app namespace: CSI Driver mounts the token and creates K8s Secret &lt;code&gt;bw-auth-token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;BitwardenSecret in the same namespace: &lt;code&gt;authToken.secretName: bw-auth-token&lt;/code&gt;. The sm-operator reads this token and calls the Bitwarden API.&lt;/li&gt;
&lt;li&gt;sm-operator creates the output K8s Secret (e.g. &lt;code&gt;atlantis-1-vcs&lt;/code&gt;) in the app’s namespace. Atlantis (or any app) uses it directly.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  4. Bitwarden Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Machine Account and token:&lt;/strong&gt; Bitwarden Admin → Machine Accounts → Create Access Token. Copy the token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-app machine token (optional):&lt;/strong&gt; For isolation, create a separate token per app and store in &lt;code&gt;bitwarden/sm-operator/&amp;lt;app&amp;gt;/machine-token&lt;/code&gt; in AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App secrets:&lt;/strong&gt; In Bitwarden Secrets Manager, create the secrets your apps need. For Atlantis (GitHub App): webhook secret, private key (&lt;code&gt;key.pem&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy IDs:&lt;/strong&gt; From Bitwarden Admin → Settings → Organization, copy &lt;code&gt;organizationId&lt;/code&gt;. For each secret, copy its &lt;code&gt;bwSecretId&lt;/code&gt; (UUID).&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  5. Terraform
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Requirement:&lt;/strong&gt; EKS module must enable Secrets Manager with Pod Identity. Add an association &lt;strong&gt;per app namespace&lt;/strong&gt; (e.g. &lt;code&gt;{ namespace = "atlantis-1", service_account = "awssm-sync" }&lt;/code&gt;) and prefix &lt;code&gt;bitwarden/sm-operator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create the AWS Secrets Manager secret per app. Pass the token via &lt;code&gt;-var&lt;/code&gt; or &lt;code&gt;TF_VAR_&lt;/code&gt;. Never commit it.&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;"bitwarden_sm_machine_token_atlantis"&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;"Bitwarden SM machine token for atlantis-1"&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;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"REPLACE_WITH_REAL_TOKEN"&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_secretsmanager_secret"&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden_atlantis"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden/sm-operator/atlantis-1/machine-token"&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;"Bitwarden SM machine token for atlantis-1"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&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;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_secretsmanager_secret_version"&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden_atlantis"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;secret_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_secretsmanager_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bitwarden_atlantis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;secret_string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;token&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;bitwarden_sm_machine_token_atlantis&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;h3&gt;
  
  
  6. Per-App Manifests (Atlantis Example)
&lt;/h3&gt;

&lt;p&gt;Each app gets its own overlay with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SecretProviderClass&lt;/li&gt;
&lt;li&gt;ServiceAccount + &lt;code&gt;bw-auth-token-sync&lt;/code&gt; Deployment&lt;/li&gt;
&lt;li&gt;BitwardenSecret&lt;/li&gt;
&lt;li&gt;The app itself (e.g. Atlantis Helm chart)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6.1 SecretProviderClass
&lt;/h4&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;secrets-store.csi.x-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;SecretProviderClass&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;bitwarden-sm-token&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;atlantis-1&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;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-southeast-2&lt;/span&gt;
    &lt;span class="na"&gt;usePodIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;- objectName: "bitwarden/sm-operator/atlantis-1/machine-token"&lt;/span&gt;
        &lt;span class="s"&gt;objectType: "secretsmanager"&lt;/span&gt;
        &lt;span class="s"&gt;jmesPath:&lt;/span&gt;
          &lt;span class="s"&gt;- path: token&lt;/span&gt;
            &lt;span class="s"&gt;objectAlias: token&lt;/span&gt;
  &lt;span class="na"&gt;secretObjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token&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;Opaque&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;objectName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.2 ServiceAccount + bw-auth-token-sync
&lt;/h4&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;ServiceAccount&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;awssm-sync&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;atlantis-1&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="nn"&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;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;bw-auth-token-sync&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;atlantis-1&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awssm-sync&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;pause&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.k8s.io/pause:3.9&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;requests&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;1m&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;4Mi&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&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;secrets-store&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/mnt/secrets-store"&lt;/span&gt;
              &lt;span class="na"&gt;readOnly&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;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&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;secrets-store&lt;/span&gt;
          &lt;span class="na"&gt;csi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets-store.csi.k8s.io&lt;/span&gt;
            &lt;span class="na"&gt;readOnly&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;volumeAttributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;secretProviderClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bitwarden-sm-token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.3 BitwardenSecret
&lt;/h4&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;k8s.bitwarden.com/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;BitwardenSecret&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;atlantis-1-vcs&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;atlantis-1&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;0"&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;organizationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_ORG_ID"&lt;/span&gt;
  &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlantis-1-vcs&lt;/span&gt;
  &lt;span class="na"&gt;onlyMappedSecrets&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;map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github_secret&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_WEBHOOK_SECRET_ID"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;key.pem&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_PRIVATE_KEY_ID"&lt;/span&gt;
  &lt;span class="na"&gt;authToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token&lt;/span&gt;
    &lt;span class="na"&gt;secretKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.4 Atlantis Helm values
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application-patch.yaml&lt;/span&gt;
&lt;span class="na"&gt;vcsSecretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlantis-1-vcs&lt;/span&gt;
&lt;span class="na"&gt;githubApp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_GH_APP_ID"&lt;/span&gt;
  &lt;span class="na"&gt;installationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_GH_INSTALLATION_ID"&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;false&lt;/span&gt;   &lt;span class="c1"&gt;# Enable when you have ingress + atlantisUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;argocd.argoproj.io/sync-wave: "5"&lt;/code&gt; on the Atlantis Application so it is created after the BitwardenSecret and &lt;code&gt;bw-auth-token-sync&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Sync Waves (Ordering)
&lt;/h3&gt;

&lt;p&gt;To avoid the app starting before secrets exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;-2:&lt;/strong&gt; ServiceAccount &lt;code&gt;awssm-sync&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;-1:&lt;/strong&gt; SecretProviderClass, &lt;code&gt;bw-auth-token-sync&lt;/code&gt; Deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0:&lt;/strong&gt; BitwardenSecret, ConfigMaps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5:&lt;/strong&gt; Atlantis Application (Helm)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  8. Validation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify secrets in app namespace&lt;/span&gt;
kubectl get secret atlantis-1-vcs bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1

&lt;span class="c"&gt;# BitwardenSecret status&lt;/span&gt;
kubectl get bitwardensecret atlantis-1-vcs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
kubectl get bitwardensecret atlantis-1-vcs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.conditions[?(@.type=="SuccessfulSync")].status}'&lt;/span&gt;

&lt;span class="c"&gt;# Output secret keys (should show github_secret, key.pem)&lt;/span&gt;
kubectl get secret atlantis-1-vcs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'keys[]'&lt;/span&gt;

&lt;span class="c"&gt;# Atlantis pod&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;atlantis &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  9. Local Access (Port-Forward)
&lt;/h3&gt;

&lt;p&gt;The Atlantis Service exposes port 80 (targetPort 4141):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/atlantis-1 &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 4141:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:4141" rel="noopener noreferrer"&gt;http://localhost:4141&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Force Sync (Token Refresh)
&lt;/h3&gt;

&lt;p&gt;When the machine token in AWS Secrets Manager changes:&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;# Per-app namespace&lt;/span&gt;
kubectl delete secret bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--grace-period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s
kubectl rollout restart deployment/sm-operator-controller-manager &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl rollout restart statefulset atlantis-1 &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  11. Adding Another App
&lt;/h3&gt;

&lt;p&gt;For each new app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create app directory: &lt;code&gt;apps/&amp;lt;app&amp;gt;/overlays/&amp;lt;cluster&amp;gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add SecretProviderClass (AWS path: &lt;code&gt;bitwarden/sm-operator/&amp;lt;app&amp;gt;/machine-token&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;bw-auth-token-sync&lt;/code&gt; (SA + Deployment)&lt;/li&gt;
&lt;li&gt;Add BitwardenSecret with the app’s secret mapping&lt;/li&gt;
&lt;li&gt;Add Terraform: &lt;code&gt;{ namespace = "&amp;lt;app&amp;gt;", service_account = "awssm-sync" }&lt;/code&gt; + AWS secret&lt;/li&gt;
&lt;li&gt;Deploy the app (Helm, etc.) with the created secret&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  12. Summary: Copy-Paste
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Validation:&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;kubectl get secret atlantis-1-vcs bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
kubectl get bitwardensecret atlantis-1-vcs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.conditions[?(@.type=="SuccessfulSync")].status}'&lt;/span&gt;
kubectl get secret atlantis-1-vcs &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'keys[]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Port-forward:&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;kubectl port-forward svc/atlantis-1 &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 4141:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Force sync (token changed):&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;kubectl delete secret bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--grace-period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1 &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s
kubectl rollout restart deployment/sm-operator-controller-manager &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl rollout restart statefulset atlantis-1 &lt;span class="nt"&gt;-n&lt;/span&gt; atlantis-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  13. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;code&gt;CreateContainerConfigError&lt;/code&gt; – Secret &lt;code&gt;atlantis-1-vcs&lt;/code&gt; missing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Wait for sm-operator to sync BitwardenSecret; or restart pod after secret exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Pod Identity / token association error&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Add &lt;code&gt;{ namespace = "atlantis-1", service_account = "awssm-sync" }&lt;/code&gt; to &lt;code&gt;secrets_manager_associations&lt;/code&gt; in Terraform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;code&gt;ResourceNotFoundException&lt;/code&gt; – AWS secret missing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Create &lt;code&gt;bitwarden/sm-operator/atlantis-1/machine-token&lt;/code&gt; in Secrets Manager (JSON: &lt;code&gt;{"token":"&amp;lt;value&amp;gt;"}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;code&gt;Error: --gh-app-id/--gh-app-key-file...&lt;/code&gt; – VCS secret not reaching container&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Ensure &lt;code&gt;vcsSecretName&lt;/code&gt; and &lt;code&gt;githubApp.id&lt;/code&gt;/&lt;code&gt;installationId&lt;/code&gt; in Helm values; verify &lt;code&gt;atlantis-1-vcs&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Argo CD app stuck "Progressing"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Ingress without controller. Set &lt;code&gt;ingress.enabled: false&lt;/code&gt; until ingress is configured.&lt;/p&gt;




&lt;h3&gt;
  
  
  14. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitwarden.com/help/secrets-manager-kubernetes-operator/" rel="noopener noreferrer"&gt;Bitwarden SM Operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://secrets-store-csi-driver.sigs.k8s.io/" rel="noopener noreferrer"&gt;Secrets Store CSI Driver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/runatlantis/helm-charts" rel="noopener noreferrer"&gt;Atlantis Helm Chart&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bitwarden</category>
      <category>secretsmanager</category>
      <category>eks</category>
      <category>atlantis</category>
    </item>
    <item>
      <title>Bitwarden Secrets Manager on EKS – End-to-End Integration</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 01 Mar 2026 18:41:10 +0000</pubDate>
      <link>https://dev.to/jajera/bitwarden-secrets-manager-on-eks-end-to-end-integration-10d3</link>
      <guid>https://dev.to/jajera/bitwarden-secrets-manager-on-eks-end-to-end-integration-10d3</guid>
      <description>&lt;h2&gt;
  
  
  Bitwarden Secrets Manager on EKS – End-to-End Integration
&lt;/h2&gt;

&lt;p&gt;Sync secrets from Bitwarden Secrets Manager into Kubernetes on EKS using the sm-operator, AWS Secrets Manager for the machine token, and the Secrets Store CSI Driver. This guide walks through Terraform, manifests, Argo CD, validation, and force-sync procedures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Use placeholder values for org IDs and secret IDs. Never commit real tokens. For production, follow least-privilege IAM and rotation practices.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrates Bitwarden Secrets Manager with EKS via sm-operator, AWS Secrets Manager, and Secrets Store CSI Driver&lt;/li&gt;
&lt;li&gt;Stores machine token in AWS Secrets Manager; CSI Driver mounts it; sm-operator syncs Bitwarden secrets into K8s Secrets&lt;/li&gt;
&lt;li&gt;Walks through Terraform (EKS + secret), flat manifests, Argo CD Application, validation, and force-sync&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;EKS cluster with &lt;strong&gt;Secrets Store CSI Driver&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;Terraform-managed EKS (e.g. terraform-aws-eks-basic or equivalent)&lt;/li&gt;
&lt;li&gt;Bitwarden Secrets Manager organization with Machine Account&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Architecture Overview
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bitwarden SM (Machine Account + App Secrets)
        │
        │ machine token
        ▼
AWS Secrets Manager (bitwarden/sm-operator/machine-token)
        │
        │ Pod Identity + CSI
        ▼
Secrets Store CSI Driver → creates bw-auth-token
        │
        ▼
sm-operator (reads BitwardenSecret CR, uses bw-auth-token)
        │
        │ Bitwarden API fetches secrets
        ▼
github-app-secrets (K8s Secret)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Machine token stored in AWS Secrets Manager as &lt;code&gt;{"token":"&amp;lt;value&amp;gt;"}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSI Driver mounts it into a pod; creates K8s Secret &lt;code&gt;bw-auth-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;sm-operator uses &lt;code&gt;bw-auth-token&lt;/code&gt; to auth to Bitwarden API and sync secrets into &lt;code&gt;github-app-secrets&lt;/code&gt; (or your chosen output secret)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Bitwarden Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Machine Account and token:&lt;/strong&gt; Bitwarden Admin → Machine Accounts → Create Access Token. Copy the token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App secrets:&lt;/strong&gt; In Bitwarden Secrets Manager, create the secrets your apps need (e.g. &lt;code&gt;github-app-id&lt;/code&gt;, &lt;code&gt;github-app-key&lt;/code&gt;, &lt;code&gt;github-app-webhook-secret&lt;/code&gt;, &lt;code&gt;github-app-installation-id&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy IDs:&lt;/strong&gt; From Bitwarden Admin → Settings → Organization, copy &lt;code&gt;organizationId&lt;/code&gt;. For each secret, copy its &lt;code&gt;bwSecretId&lt;/code&gt; (UUID).&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  5. Terraform
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Requirement:&lt;/strong&gt; EKS module must enable Secrets Manager with Pod Identity for &lt;code&gt;sm-operator-system&lt;/code&gt; / &lt;code&gt;awssm-sync&lt;/code&gt; and secret prefix &lt;code&gt;bitwarden/sm-operator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create the AWS Secrets Manager secret and pass the token via &lt;code&gt;-var&lt;/code&gt; or &lt;code&gt;TF_VAR_bitwarden_sm_machine_token&lt;/code&gt;. Never commit it.&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;"bitwarden_sm_machine_token"&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;"Bitwarden SM machine token from Machine Account → Create Access Token"&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;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"REPLACE_WITH_REAL_TOKEN"&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_secretsmanager_secret"&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden_sm_token"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden/sm-operator/machine-token"&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;"Bitwarden Secrets Manager machine token for sm-operator"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&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;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_secretsmanager_secret_version"&lt;/span&gt; &lt;span class="s2"&gt;"bitwarden_sm_token"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;secret_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_secretsmanager_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bitwarden_sm_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;secret_string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;token&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;bitwarden_sm_machine_token&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;h3&gt;
  
  
  6. Deploy sm-operator
&lt;/h3&gt;

&lt;p&gt;Save as &lt;code&gt;sm-operator.yaml&lt;/code&gt; and apply. Replace placeholders in BitwardenSecret; adjust &lt;code&gt;region&lt;/code&gt; if needed.&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;# Namespace for sm-operator and CSI resources&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;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;Namespace&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;sm-operator-system&lt;/span&gt;
  &lt;span class="na"&gt;labels&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;sm-operator-system&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sm-operator&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# ServiceAccount with Pod Identity for AWS Secrets Manager access&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;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;ServiceAccount&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;awssm-sync&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;sm-operator-system&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="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Fetches machine token from AWS SM and creates K8s Secret bw-auth-token&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;secrets-store.csi.x-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;SecretProviderClass&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;bitwarden-sm-token&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;sm-operator-system&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;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-southeast-2&lt;/span&gt;
    &lt;span class="na"&gt;usePodIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;objects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;- objectName: "bitwarden/sm-operator/machine-token"&lt;/span&gt;
        &lt;span class="s"&gt;objectType: "secretsmanager"&lt;/span&gt;
        &lt;span class="s"&gt;jmesPath:&lt;/span&gt;
          &lt;span class="s"&gt;- path: token&lt;/span&gt;
            &lt;span class="s"&gt;objectAlias: token&lt;/span&gt;
  &lt;span class="na"&gt;secretObjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token&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;Opaque&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;objectName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Pod that triggers CSI mount; creates bw-auth-token used by sm-operator&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;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;bw-auth-token-sync&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;sm-operator-system&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token-sync&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;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awssm-sync&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;pause&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.k8s.io/pause:3.9&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;requests&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;1m&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;4Mi&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&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;secrets-store&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/mnt/secrets-store"&lt;/span&gt;
              &lt;span class="na"&gt;readOnly&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;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&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;secrets-store&lt;/span&gt;
          &lt;span class="na"&gt;csi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets-store.csi.k8s.io&lt;/span&gt;
            &lt;span class="na"&gt;readOnly&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;volumeAttributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;secretProviderClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bitwarden-sm-token&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Maps Bitwarden secrets to github-app-secrets K8s Secret&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;k8s.bitwarden.com/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;BitwardenSecret&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;bitwarden-secret&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;sm-operator-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;organizationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_ORG_ID"&lt;/span&gt;
  &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-app-secrets&lt;/span&gt;
  &lt;span class="na"&gt;onlyMappedSecrets&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;map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-app-id&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_SECRET_ID_1"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-app-key&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_SECRET_ID_2"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-app-webhook-secret&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_SECRET_ID_3"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-app-installation-id&lt;/span&gt;
      &lt;span class="na"&gt;bwSecretId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPLACE_SECRET_ID_4"&lt;/span&gt;
  &lt;span class="na"&gt;authToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bw-auth-token&lt;/span&gt;
    &lt;span class="na"&gt;secretKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Argo CD Application; deploys sm-operator Helm chart&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;sm-operator&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;argocd&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;platform&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://charts.bitwarden.com/&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sm-operator&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;2.0.0&lt;/span&gt;
    &lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;settings:&lt;/span&gt;
          &lt;span class="s"&gt;bwSecretsManagerRefreshInterval: 300&lt;/span&gt;
          &lt;span class="s"&gt;cloudRegion: US&lt;/span&gt;
          &lt;span class="s"&gt;replicas: 1&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;sm-operator-system&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;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateNamespace=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Validation
&lt;/h3&gt;

&lt;p&gt;After Argo CD syncs the app:&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;# BitwardenSecret status&lt;/span&gt;
kubectl get bitwardensecret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl get bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.conditions[?(@.type=="SuccessfulSync")].status}'&lt;/span&gt;

&lt;span class="c"&gt;# Output secret keys&lt;/span&gt;
kubectl get secret github-app-secrets &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'keys[]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected: &lt;code&gt;SuccessfulSync&lt;/code&gt; status &lt;code&gt;True&lt;/code&gt; and the mapped keys listed.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Force Sync (Token Refresh)
&lt;/h3&gt;

&lt;p&gt;When the machine token in AWS Secrets Manager changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete secret bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--grace-period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s
kubectl rollout restart deployment/sm-operator-controller-manager &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When only BitwardenSecret mapping changes (e.g. new secrets):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
&lt;span class="c"&gt;# Argo CD recreates it; wait for sync&lt;/span&gt;
kubectl annotate bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  force-reconcile&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  9. Summary: Copy-Paste
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Validation (after sync):&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;kubectl get bitwardensecret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl get bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.conditions[?(@.type=="SuccessfulSync")].status}'&lt;/span&gt;
kubectl get secret github-app-secrets &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'keys[]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Force sync (machine token changed):&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;kubectl delete secret bw-auth-token &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--grace-period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
kubectl &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ready pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bw-auth-token-sync &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s
kubectl rollout restart deployment/sm-operator-controller-manager &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Force sync (BitwardenSecret mapping changed):&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;kubectl delete bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system
kubectl annotate bitwardensecret bitwarden-secret &lt;span class="nt"&gt;-n&lt;/span&gt; sm-operator-system force-reconcile&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  10. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Has an invalid identifier&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; One or more &lt;code&gt;bwSecretId&lt;/code&gt; UUIDs are wrong. Verify each ID in Bitwarden Secrets Manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; CSI secret not created&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check Pod Identity for SA &lt;code&gt;awssm-sync&lt;/code&gt; in &lt;code&gt;sm-operator-system&lt;/code&gt;; ensure SecretProviderClass and &lt;code&gt;bw-auth-token-sync&lt;/code&gt; pod exist. Check CSI driver logs: &lt;code&gt;kubectl logs -n kube-system -l app=csi-secrets-store&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; No changes / Skipping sync&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Delete the BitwardenSecret; Argo recreates it and the operator performs a fresh reconcile.&lt;/p&gt;




&lt;h3&gt;
  
  
  11. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitwarden.com/help/secrets-manager-kubernetes-operator/" rel="noopener noreferrer"&gt;Bitwarden SM Operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://secrets-store-csi-driver.sigs.k8s.io/" rel="noopener noreferrer"&gt;Secrets Store CSI Driver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bitwarden</category>
      <category>secretsmanager</category>
      <category>eks</category>
      <category>argocd</category>
    </item>
    <item>
      <title>FastSchema on EKS – Install and Test</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 01 Mar 2026 04:09:39 +0000</pubDate>
      <link>https://dev.to/jajera/fastschema-on-eks-install-and-test-10o3</link>
      <guid>https://dev.to/jajera/fastschema-on-eks-install-and-test-10o3</guid>
      <description>&lt;h2&gt;
  
  
  FastSchema on EKS – Install and Test
&lt;/h2&gt;

&lt;p&gt;Deploy FastSchema via Argo CD on EKS and access it locally with port-forward. Uses SQLite with EBS-backed PVC. First-run setup token from pod logs.&lt;/p&gt;

&lt;p&gt;FastSchema has &lt;strong&gt;no official Helm chart&lt;/strong&gt;. This guide uses a community chart at &lt;a href="https://github.com/k8sforge/fastschema-chart" rel="noopener noreferrer"&gt;k8sforge/fastschema-chart&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This setup is for evaluation, not production-ready. For production, use external database (MySQL/PostgreSQL), configure auth providers, and follow security best practices.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys FastSchema using the community Helm chart via an Argo CD Application&lt;/li&gt;
&lt;li&gt;Persists data with SQLite on EBS-backed PVC&lt;/li&gt;
&lt;li&gt;Uses port-forward for local access (no ingress required)&lt;/li&gt;
&lt;li&gt;Walks through first-run setup: token from pod logs, create admin account, verify storage&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;kubectl configured for your EKS cluster&lt;/li&gt;
&lt;li&gt;Argo CD installed&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt; configured for your EKS cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install FastSchema
&lt;/h3&gt;

&lt;p&gt;Save the manifest below as &lt;code&gt;fastschema-application.yaml&lt;/code&gt; and apply:&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; fastschema-application.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait until the Application shows &lt;strong&gt;Synced&lt;/strong&gt; in the Argo CD UI or CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manifest:&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;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;AppProject&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;devops&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;argocd&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;clusterResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DevOps-managed applications&lt;/span&gt;
  &lt;span class="na"&gt;destinations&lt;/span&gt;&lt;span class="pi"&gt;:&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
      &lt;span class="na"&gt;server&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="na"&gt;namespaceResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;sourceRepos&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="nn"&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;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;Namespace&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;fastschema&lt;/span&gt;
  &lt;span class="na"&gt;labels&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;fastschema&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devops&lt;/span&gt;
&lt;span class="nn"&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;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;fastschema&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;argocd&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;devops&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://k8sforge.github.io/fastschema-chart&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastschema&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;0.1.4&lt;/span&gt;
    &lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;service:&lt;/span&gt;
          &lt;span class="s"&gt;type: ClusterIP&lt;/span&gt;
        &lt;span class="s"&gt;persistence:&lt;/span&gt;
          &lt;span class="s"&gt;enabled: true&lt;/span&gt;
          &lt;span class="s"&gt;size: 10Gi&lt;/span&gt;
          &lt;span class="s"&gt;storageClassName: ebs-sc&lt;/span&gt;
          &lt;span class="s"&gt;accessModes:&lt;/span&gt;
            &lt;span class="s"&gt;- ReadWriteOnce&lt;/span&gt;
        &lt;span class="s"&gt;appBaseUrl: "http://localhost:8000"&lt;/span&gt;
        &lt;span class="s"&gt;appDashUrl: "http://localhost:8000/dash"&lt;/span&gt;
        &lt;span class="s"&gt;resources:&lt;/span&gt;
          &lt;span class="s"&gt;limits:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 500m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 512Mi&lt;/span&gt;
          &lt;span class="s"&gt;requests:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 100m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 256Mi&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;fastschema&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;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateNamespace=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Omit AppProject and Namespace if you already have them (e.g. via devops-apps ApplicationSet).&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Test Access
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Port-forward
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/fastschema &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema 8000:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get setup token (first run)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema &lt;span class="nt"&gt;-l&lt;/span&gt; app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;fastschema &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for: &lt;code&gt;Visit the following URL to setup the app: http://localhost:8000/dash/setup/?token=...&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup and login
&lt;/h4&gt;

&lt;p&gt;Open the URL from the logs. Complete setup to create admin account. Log in at &lt;a href="http://localhost:8000/dash" rel="noopener noreferrer"&gt;http://localhost:8000/dash&lt;/a&gt; with the credentials you created.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Verify EBS Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema deploy/fastschema &lt;span class="nt"&gt;--&lt;/span&gt; sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the pod:&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="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /fastschema/data
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /fastschema/data
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"storage-test-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /fastschema/data/storage-test.txt
&lt;span class="nb"&gt;cat&lt;/span&gt; /fastschema/data/storage-test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit, then from your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema &lt;span class="nt"&gt;-l&lt;/span&gt; app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;fastschema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the new pod to be ready, exec in again and run:&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="nb"&gt;cat&lt;/span&gt; /fastschema/data/storage-test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file and content persist, the PVC is working.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Apply manifest&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; fastschema-application.yaml

&lt;span class="c"&gt;# 2. Wait for sync (argocd app get fastschema)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Port-forward and get token&lt;/span&gt;
kubectl port-forward svc/fastschema &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema 8000:8000 &amp;amp;
kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; fastschema &lt;span class="nt"&gt;-l&lt;/span&gt; app.kubernetes.io/name&lt;span class="o"&gt;=&lt;/span&gt;fastschema &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the setup URL from logs, create admin account, then log in at &lt;a href="http://localhost:8000/dash" rel="noopener noreferrer"&gt;http://localhost:8000/dash&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; No setup token in logs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Wait for pod to be ready. Token prints once on startup. If setup already done, use login at /dash with your credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; PVC stuck Pending&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Ensure &lt;code&gt;storageClassName: ebs-sc&lt;/code&gt; exists. Check EBS CSI driver is installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Service not found&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; &lt;code&gt;kubectl get svc -n fastschema&lt;/code&gt;. Ensure app is synced.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastSchema&lt;/strong&gt;: &lt;a href="https://fastschema.com" rel="noopener noreferrer"&gt;https://fastschema.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt;: &lt;a href="https://fastschema.com/docs/configuration.html" rel="noopener noreferrer"&gt;https://fastschema.com/docs/configuration.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chart&lt;/strong&gt;: &lt;a href="https://github.com/k8sforge/fastschema-chart" rel="noopener noreferrer"&gt;https://github.com/k8sforge/fastschema-chart&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>fastschema</category>
      <category>eks</category>
      <category>argocd</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Headlamp on EKS – Install and Test</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Feb 2026 22:21:50 +0000</pubDate>
      <link>https://dev.to/jajera/headlamp-on-eks-install-and-test-113b</link>
      <guid>https://dev.to/jajera/headlamp-on-eks-install-and-test-113b</guid>
      <description>&lt;h2&gt;
  
  
  Headlamp on EKS – Install and Test
&lt;/h2&gt;

&lt;p&gt;Need a lightweight Kubernetes UI without the overhead of kubectl or cloud consoles? &lt;strong&gt;Headlamp&lt;/strong&gt; runs in-cluster, gives you real-time visibility into workloads, logs, and events—and it's free and open source. This guide deploys it via Argo CD with &lt;strong&gt;Service Account token auth&lt;/strong&gt;: no OIDC setup, no manual secrets, and access via simple port-forward. Secure, easy to run, and GitOps-friendly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This setup uses &lt;code&gt;cluster-admin&lt;/code&gt; for simplicity—ideal for learning or individual use. For production, configure proper role-based access (RBAC) and restrict the Service Account to least-privilege roles.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys Headlamp using the official Helm chart via an Argo CD Application&lt;/li&gt;
&lt;li&gt;Creates a Service Account &lt;code&gt;headlamp&lt;/code&gt; with &lt;code&gt;cluster-admin&lt;/code&gt; for token-based login&lt;/li&gt;
&lt;li&gt;Persists data with EBS-backed PVC&lt;/li&gt;
&lt;li&gt;Uses port-forward for local access (no ingress required)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;EKS cluster running with &lt;strong&gt;kubectl&lt;/strong&gt; context set&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt; configured with context set to your EKS cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed and syncing Applications&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install Headlamp
&lt;/h3&gt;

&lt;p&gt;Save the manifest below as &lt;code&gt;headlamp-application.yaml&lt;/code&gt; and apply:&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; headlamp-application.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Argo CD will create the Application and sync Headlamp. Wait until the Application shows &lt;strong&gt;Synced&lt;/strong&gt; in the Argo CD UI or CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manifest:&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;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;AppProject&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;platform&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;argocd&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;clusterResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;destinations&lt;/span&gt;&lt;span class="pi"&gt;:&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
      &lt;span class="na"&gt;server&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="na"&gt;namespaceResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;sourceRepos&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="nn"&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;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;Namespace&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;headlamp&lt;/span&gt;
  &lt;span class="na"&gt;labels&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;headlamp&lt;/span&gt;
&lt;span class="nn"&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;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;headlamp&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;argocd&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;platform&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://kubernetes-sigs.github.io/headlamp/&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;headlamp&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;0.40.0&lt;/span&gt;
    &lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;service:&lt;/span&gt;
          &lt;span class="s"&gt;type: ClusterIP&lt;/span&gt;
        &lt;span class="s"&gt;config:&lt;/span&gt;
          &lt;span class="s"&gt;inCluster: true&lt;/span&gt;
          &lt;span class="s"&gt;oidc:&lt;/span&gt;
            &lt;span class="s"&gt;secret:&lt;/span&gt;
              &lt;span class="s"&gt;create: false&lt;/span&gt;
        &lt;span class="s"&gt;serviceAccount:&lt;/span&gt;
          &lt;span class="s"&gt;create: true&lt;/span&gt;
          &lt;span class="s"&gt;name: headlamp&lt;/span&gt;
        &lt;span class="s"&gt;clusterRoleBinding:&lt;/span&gt;
          &lt;span class="s"&gt;clusterRoleName: cluster-admin&lt;/span&gt;
        &lt;span class="s"&gt;persistentVolumeClaim:&lt;/span&gt;
          &lt;span class="s"&gt;enabled: true&lt;/span&gt;
          &lt;span class="s"&gt;size: 10Gi&lt;/span&gt;
          &lt;span class="s"&gt;storageClassName: ebs-sc&lt;/span&gt;
          &lt;span class="s"&gt;accessModes:&lt;/span&gt;
            &lt;span class="s"&gt;- ReadWriteOnce&lt;/span&gt;
        &lt;span class="s"&gt;resources:&lt;/span&gt;
          &lt;span class="s"&gt;limits:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 200m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 256Mi&lt;/span&gt;
          &lt;span class="s"&gt;requests:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 100m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 128Mi&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;headlamp&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;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateNamespace=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;serviceAccount.create: true&lt;/code&gt; block creates the &lt;code&gt;headlamp&lt;/code&gt; Service Account—this is what &lt;code&gt;kubectl create token headlamp -n headlamp&lt;/code&gt; uses for login. Adjust &lt;code&gt;storageClassName&lt;/code&gt; if your EKS cluster uses a different EBS storage class. Omit the AppProject and Namespace if you already have them.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Test Access
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Port-forward
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/headlamp &lt;span class="nt"&gt;-n&lt;/span&gt; headlamp 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create token
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create token headlamp &lt;span class="nt"&gt;-n&lt;/span&gt; headlamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Login
&lt;/h4&gt;

&lt;p&gt;Open &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; and paste the token when Headlamp prompts for authentication.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Apply manifest&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; headlamp-application.yaml

&lt;span class="c"&gt;# 2. Wait for sync (check Argo CD UI or: argocd app get headlamp)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Port-forward and get token&lt;/span&gt;
kubectl port-forward svc/headlamp &lt;span class="nt"&gt;-n&lt;/span&gt; headlamp 8080:80 &amp;amp;
kubectl create token headlamp &lt;span class="nt"&gt;-n&lt;/span&gt; headlamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; and paste the token when prompted.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Application stuck in Syncing or OutOfSync&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check Argo CD logs and the Application status. Ensure the Headlamp Helm repo is reachable and &lt;code&gt;storageClassName: ebs-sc&lt;/code&gt; exists in your cluster. If using a different storage class, update the manifest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Token invalid or login fails&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Ensure the Service Account exists: &lt;code&gt;kubectl get sa headlamp -n headlamp&lt;/code&gt;. Re-run &lt;code&gt;kubectl create token headlamp -n headlamp&lt;/code&gt; to generate a fresh token (tokens expire).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Argo CD ingress stuck or inaccessible&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; See your cluster's Argo CD troubleshooting docs.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Headlamp&lt;/strong&gt;: &lt;a href="https://headlamp.dev" rel="noopener noreferrer"&gt;https://headlamp.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installation (Service Account token)&lt;/strong&gt;: &lt;a href="https://headlamp.dev/docs/latest/installation/#create-a-service-account-token" rel="noopener noreferrer"&gt;https://headlamp.dev/docs/latest/installation/#create-a-service-account-token&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt;: &lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;https://argo-cd.readthedocs.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>headlamp</category>
      <category>eks</category>
      <category>argocd</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Gatekeeper Disallow Default Namespace – Create, Deploy &amp; Test Guide</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Fri, 27 Feb 2026 21:40:15 +0000</pubDate>
      <link>https://dev.to/jajera/gatekeeper-disallow-default-namespace-create-deploy-test-guide-35of</link>
      <guid>https://dev.to/jajera/gatekeeper-disallow-default-namespace-create-deploy-test-guide-35of</guid>
      <description>&lt;h2&gt;
  
  
  Gatekeeper Disallow Default Namespace – Create, Deploy &amp;amp; Test Guide
&lt;/h2&gt;

&lt;p&gt;A step-by-step guide to building and deploying a custom Gatekeeper policy that blocks workloads from being created in the Kubernetes &lt;code&gt;default&lt;/code&gt; namespace.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocks Pods, Services, Deployments, ConfigMaps, Secrets, and similar resources from being created in &lt;code&gt;default&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Runs in &lt;strong&gt;dryrun&lt;/strong&gt; (audit-only) or &lt;strong&gt;deny&lt;/strong&gt; (block) mode&lt;/li&gt;
&lt;li&gt;Can be tuned to target specific resource types&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Kubernetes cluster (1.19+)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/install/" rel="noopener noreferrer"&gt;Gatekeeper&lt;/a&gt; installed (Helm or manifest)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; configured for your cluster&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Kubernetes cluster (1.19+)&lt;/li&gt;
&lt;li&gt;Gatekeeper installed (&lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/install/" rel="noopener noreferrer"&gt;install guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; configured for your cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify Gatekeeper is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; gatekeeper-system
kubectl get constrainttemplates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. How It Works
&lt;/h3&gt;

&lt;p&gt;Gatekeeper uses two resource types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&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;&lt;strong&gt;ConstraintTemplate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Defines the policy logic (Rego) and the CRD for the constraint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Constraint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instantiates the policy with match rules and enforcement mode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gatekeeper's controller watches ConstraintTemplates, creates CRDs for them, and then Constraints of that kind can be applied. The order matters: &lt;strong&gt;ConstraintTemplate first, then Constraint&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Folder Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatekeeper-policies/
├── constraint-templates/
│   └── disallow-default-namespace.yaml
└── constraints/
    └── disallow-default-namespace.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Create the ConstraintTemplate
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;constraint-templates/disallow-default-namespace.yaml&lt;/code&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;templates.gatekeeper.sh/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;ConstraintTemplate&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;k8sdisallowdefaultnamespace&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;metadata.gatekeeper.sh/title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Disallow&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Default&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Namespace"&lt;/span&gt;
    &lt;span class="na"&gt;metadata.gatekeeper.sh/version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Disallows&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deploying&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;namespaced&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;resources&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;namespace."&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;crd&lt;/span&gt;&lt;span class="pi"&gt;:&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;names&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;K8sDisallowDefaultNamespace&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="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admission.k8s.gatekeeper.sh&lt;/span&gt;
      &lt;span class="na"&gt;rego&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;package k8sdisallowdefaultnamespace&lt;/span&gt;

        &lt;span class="s"&gt;violation[{"msg": msg}] {&lt;/span&gt;
          &lt;span class="s"&gt;input.review.object.metadata.namespace == "default"&lt;/span&gt;
          &lt;span class="s"&gt;msg := "Deploying to the default namespace is not allowed"&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;metadata.name&lt;/code&gt;&lt;/strong&gt;: Must be lowercase; Gatekeeper derives the constraint kind from it (&lt;code&gt;K8sDisallowDefaultNamespace&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;spec.crd.spec.names.kind&lt;/code&gt;&lt;/strong&gt;: Defines the constraint kind created by this template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;package&lt;/code&gt;&lt;/strong&gt;: Must match &lt;code&gt;metadata.name&lt;/code&gt; (e.g. &lt;code&gt;k8sdisallowdefaultnamespace&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;violation&lt;/code&gt;&lt;/strong&gt;: Returned when the policy fails. &lt;code&gt;input.review.object&lt;/code&gt; is the resource being created or updated.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. Create the Constraint
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;constraints/disallow-default-namespace.yaml&lt;/code&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;constraints.gatekeeper.sh/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;K8sDisallowDefaultNamespace&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;disallow-default-namespace&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;enforcementAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dryrun&lt;/span&gt;   &lt;span class="c1"&gt;# dryrun = audit only; deny = block&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kinds&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;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&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;apps"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReplicaSet&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DaemonSet&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;extensions"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networking.k8s.io"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Ingress&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;batch"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CronJob&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kind&lt;/code&gt;&lt;/strong&gt;: Must match the kind defined in the ConstraintTemplate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;enforcementAction&lt;/code&gt;&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dryrun&lt;/code&gt;: Log violations only, do not block.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deny&lt;/code&gt;: Block violating requests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;match.kinds&lt;/code&gt;&lt;/strong&gt;: List of API groups and kinds to validate. Extend as needed.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Deploy
&lt;/h3&gt;

&lt;p&gt;Apply in this order: template first, then constraint.&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. ConstraintTemplate (creates the CRD)&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; constraint-templates/disallow-default-namespace.yaml

&lt;span class="c"&gt;# 2. Wait for the CRD to exist (a few seconds)&lt;/span&gt;
kubectl get crd k8sdisallowdefaultnamespace.constraints.gatekeeper.sh

&lt;span class="c"&gt;# 3. Constraint&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; constraints/disallow-default-namespace.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get constrainttemplate k8sdisallowdefaultnamespace
kubectl get k8sdisallowdefaultnamespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  8. Test
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Phase 1: Dryrun (audit only)
&lt;/h4&gt;

&lt;p&gt;With &lt;code&gt;enforcementAction: dryrun&lt;/code&gt;, violations are reported but not blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allowed:&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;kubectl create deployment nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx &lt;span class="nt"&gt;-n&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Violation (allowed but reported):&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;kubectl create deployment nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
&lt;span class="c"&gt;# Uses default namespace – creates successfully, but Gatekeeper logs a violation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check violations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get k8sdisallowdefaultnamespace disallow-default-namespace &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at &lt;code&gt;status.violations&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase 2: Deny (block)
&lt;/h4&gt;

&lt;p&gt;Switch to blocking mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch k8sdisallowdefaultnamespace disallow-default-namespace &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"enforcementAction":"deny"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or update the YAML and re-apply:&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;enforcementAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deny&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Blocked:&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;kubectl create deployment nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error from server ([disallow-default-namespace] Deploying to the default namespace is not allowed):
admission webhook "validation.gatekeeper.sh" denied the request: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Allowed:&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;kubectl create deployment nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx &lt;span class="nt"&gt;-n&lt;/span&gt; my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Quick test commands
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl run nginx --image=nginx&lt;/code&gt; → default namespace → &lt;strong&gt;Blocked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl run nginx --image=nginx -n prod&lt;/code&gt; → prod → &lt;strong&gt;Allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl create configmap foo --from-literal=k=v&lt;/code&gt; → default → &lt;strong&gt;Blocked&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl create configmap foo --from-literal=k=v -n staging&lt;/code&gt; → staging → &lt;strong&gt;Allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  9. Production Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Gradual rollout
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start with &lt;code&gt;dryrun&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm violations in &lt;code&gt;status.violations&lt;/code&gt; or logs.&lt;/li&gt;
&lt;li&gt;Fix or exempt workloads.&lt;/li&gt;
&lt;li&gt;Switch to &lt;code&gt;deny&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Excluding namespaces
&lt;/h4&gt;

&lt;p&gt;To skip certain namespaces, add &lt;code&gt;excludedNamespaces&lt;/code&gt; in the constraint:&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;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kinds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;...&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;excludedNamespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gatekeeper-system&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Argo CD
&lt;/h4&gt;

&lt;p&gt;If using Argo CD:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Order&lt;/strong&gt;: ConstraintTemplates before Constraints (e.g. sync-wave 1 vs 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run&lt;/strong&gt;: Add &lt;code&gt;argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true&lt;/code&gt; to constraints so sync succeeds when the CRD is not yet created.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Hardening the Rego
&lt;/h4&gt;

&lt;p&gt;For DELETE requests, &lt;code&gt;input.review.object&lt;/code&gt; may be empty. To avoid errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rego"&gt;&lt;code&gt;&lt;span class="n"&gt;violation&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"msg"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
  &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s2"&gt;"Deploying to the default namespace is not allowed"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  10. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Apply template&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; constraint-templates/disallow-default-namespace.yaml

&lt;span class="c"&gt;# 2. Wait for CRD&lt;/span&gt;
kubectl get crd k8sdisallowdefaultnamespace.constraints.gatekeeper.sh

&lt;span class="c"&gt;# 3. Apply constraint&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; constraints/disallow-default-namespace.yaml

&lt;span class="c"&gt;# 4. Test dryrun (create resources in default, check violations)&lt;/span&gt;
kubectl get k8sdisallowdefaultnamespace disallow-default-namespace &lt;span class="nt"&gt;-o&lt;/span&gt; yaml

&lt;span class="c"&gt;# 5. Switch to deny&lt;/span&gt;
kubectl patch k8sdisallowdefaultnamespace disallow-default-namespace &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"enforcementAction":"deny"}}'&lt;/span&gt;

&lt;span class="c"&gt;# 6. Test deny (confirm default namespace is blocked)&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
&lt;span class="c"&gt;# Should fail with admission webhook denied&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  11. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; &lt;code&gt;K8sDisallowDefaultNamespace.constraints.gatekeeper.sh&lt;/code&gt; not found&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The constraint CRD does not exist yet. Gatekeeper creates it when it processes the ConstraintTemplate. Apply the ConstraintTemplate first, wait until &lt;code&gt;kubectl get crd k8sdisallowdefaultnamespace.constraints.gatekeeper.sh&lt;/code&gt; succeeds, then apply the Constraint. With Argo CD, use &lt;code&gt;SkipDryRunOnMissingResource=true&lt;/code&gt; on the constraint to avoid sync failures during this window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; ConstraintTemplate stuck or errors&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Run &lt;code&gt;kubectl describe constrainttemplate k8sdisallowdefaultnamespace&lt;/code&gt; and check &lt;code&gt;status&lt;/code&gt; for Rego errors. Ensure &lt;code&gt;package&lt;/code&gt; matches the template name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; No violations in dryrun&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Confirm Gatekeeper admission webhook is active: &lt;code&gt;kubectl get validatingwebhookconfigurations&lt;/code&gt;. Try a resource in the constraint's &lt;code&gt;match.kinds&lt;/code&gt;. Inspect &lt;code&gt;status&lt;/code&gt; on the constraint for violation details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Policy blocks system components&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Add their namespaces to &lt;code&gt;excludedNamespaces&lt;/code&gt; (e.g. &lt;code&gt;kube-system&lt;/code&gt;, &lt;code&gt;gatekeeper-system&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  12. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gatekeeper Installation&lt;/strong&gt;: &lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/install/" rel="noopener noreferrer"&gt;https://open-policy-agent.github.io/gatekeeper/website/docs/install/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gatekeeper Documentation&lt;/strong&gt;: &lt;a href="https://open-policy-agent.github.io/gatekeeper/website/docs/" rel="noopener noreferrer"&gt;https://open-policy-agent.github.io/gatekeeper/website/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gatekeeper</category>
      <category>kubernetes</category>
      <category>opa</category>
      <category>policy</category>
    </item>
    <item>
      <title>Grafana on EKS – Install and Test</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Fri, 27 Feb 2026 07:06:42 +0000</pubDate>
      <link>https://dev.to/jajera/grafana-on-eks-install-and-test-5377</link>
      <guid>https://dev.to/jajera/grafana-on-eks-install-and-test-5377</guid>
      <description>&lt;h2&gt;
  
  
  Grafana on EKS – Install and Test
&lt;/h2&gt;

&lt;p&gt;Deploy Grafana via Argo CD on EKS and access it locally with port-forward. The Application manifest includes persistence, Prometheus datasource, and health probes.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys Grafana using the official Helm chart via an Argo CD Application&lt;/li&gt;
&lt;li&gt;Creates an AppProject and Application for the &lt;code&gt;grafana&lt;/code&gt; namespace&lt;/li&gt;
&lt;li&gt;Configures Prometheus as the default datasource (pre-configured for dashboards)&lt;/li&gt;
&lt;li&gt;Persists data with EBS-backed PVC&lt;/li&gt;
&lt;li&gt;Uses port-forward for local access (no ingress required)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;EKS cluster running with &lt;strong&gt;kubectl&lt;/strong&gt; context set&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; installed&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt; configured with context set to your EKS cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; installed and syncing Applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; running (e.g. &lt;code&gt;kube-prometheus-stack-prometheus.kube-prometheus-stack.svc.cluster.local:9090&lt;/code&gt;—adjust the datasource URL if yours differs)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install Grafana
&lt;/h3&gt;

&lt;p&gt;Save the manifest below as &lt;code&gt;grafana-application.yaml&lt;/code&gt; and apply:&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; grafana-application.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Argo CD will create the Application and sync Grafana (Helm chart). Wait until the Application shows &lt;strong&gt;Synced&lt;/strong&gt; in the Argo CD UI or CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manifest:&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;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;AppProject&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;platform&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;argocd&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;clusterResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;destinations&lt;/span&gt;&lt;span class="pi"&gt;:&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
      &lt;span class="na"&gt;server&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="na"&gt;namespaceResourceWhitelist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;group&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="na"&gt;kind&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="na"&gt;sourceRepos&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="nn"&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;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;Namespace&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;grafana&lt;/span&gt;
  &lt;span class="na"&gt;labels&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;grafana&lt;/span&gt;
&lt;span class="nn"&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;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;grafana&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;argocd&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;platform&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://grafana.github.io/helm-charts&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana&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;7.0.0&lt;/span&gt;
    &lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;deploymentStrategy:&lt;/span&gt;
          &lt;span class="s"&gt;type: Recreate&lt;/span&gt;
        &lt;span class="s"&gt;persistence:&lt;/span&gt;
          &lt;span class="s"&gt;enabled: true&lt;/span&gt;
          &lt;span class="s"&gt;type: pvc&lt;/span&gt;
          &lt;span class="s"&gt;storageClassName: ebs-sc&lt;/span&gt;
          &lt;span class="s"&gt;size: 10Gi&lt;/span&gt;
        &lt;span class="s"&gt;service:&lt;/span&gt;
          &lt;span class="s"&gt;type: ClusterIP&lt;/span&gt;
        &lt;span class="s"&gt;adminUser: admin&lt;/span&gt;
        &lt;span class="s"&gt;datasources:&lt;/span&gt;
          &lt;span class="s"&gt;datasources.yaml:&lt;/span&gt;
            &lt;span class="s"&gt;apiVersion: 1&lt;/span&gt;
            &lt;span class="s"&gt;datasources:&lt;/span&gt;
              &lt;span class="s"&gt;- name: Prometheus&lt;/span&gt;
                &lt;span class="s"&gt;type: prometheus&lt;/span&gt;
                &lt;span class="s"&gt;access: proxy&lt;/span&gt;
                &lt;span class="s"&gt;url: http://kube-prometheus-stack-prometheus.kube-prometheus-stack.svc.cluster.local:9090&lt;/span&gt;
                &lt;span class="s"&gt;isDefault: true&lt;/span&gt;
                &lt;span class="s"&gt;editable: true&lt;/span&gt;
        &lt;span class="s"&gt;resources:&lt;/span&gt;
          &lt;span class="s"&gt;limits:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 500m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 512Mi&lt;/span&gt;
          &lt;span class="s"&gt;requests:&lt;/span&gt;
            &lt;span class="s"&gt;cpu: 250m&lt;/span&gt;
            &lt;span class="s"&gt;memory: 256Mi&lt;/span&gt;
        &lt;span class="s"&gt;startupProbe:&lt;/span&gt;
          &lt;span class="s"&gt;httpGet:&lt;/span&gt;
            &lt;span class="s"&gt;path: /api/health&lt;/span&gt;
            &lt;span class="s"&gt;port: 3000&lt;/span&gt;
          &lt;span class="s"&gt;periodSeconds: 5&lt;/span&gt;
          &lt;span class="s"&gt;failureThreshold: 18&lt;/span&gt;
        &lt;span class="s"&gt;readinessProbe:&lt;/span&gt;
          &lt;span class="s"&gt;httpGet:&lt;/span&gt;
            &lt;span class="s"&gt;path: /api/health&lt;/span&gt;
            &lt;span class="s"&gt;port: 3000&lt;/span&gt;
          &lt;span class="s"&gt;periodSeconds: 10&lt;/span&gt;
          &lt;span class="s"&gt;failureThreshold: 3&lt;/span&gt;
        &lt;span class="s"&gt;livenessProbe:&lt;/span&gt;
          &lt;span class="s"&gt;httpGet:&lt;/span&gt;
            &lt;span class="s"&gt;path: /api/health&lt;/span&gt;
            &lt;span class="s"&gt;port: 3000&lt;/span&gt;
          &lt;span class="s"&gt;initialDelaySeconds: 30&lt;/span&gt;
          &lt;span class="s"&gt;periodSeconds: 10&lt;/span&gt;
          &lt;span class="s"&gt;failureThreshold: 10&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;grafana&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;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateNamespace=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Adjust &lt;code&gt;storageClassName&lt;/code&gt; if your EKS cluster uses a different EBS storage class. Ensure Prometheus URL matches your install (e.g. &lt;code&gt;kube-prometheus-stack-prometheus.kube-prometheus-stack.svc.cluster.local:9090&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Test Access
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Port-forward
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/grafana &lt;span class="nt"&gt;-n&lt;/span&gt; grafana 3000:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Get admin password
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret grafana &lt;span class="nt"&gt;-n&lt;/span&gt; grafana &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.admin-password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Login
&lt;/h4&gt;

&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and sign in with &lt;code&gt;admin&lt;/code&gt; and the password from above.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verify datasource
&lt;/h4&gt;

&lt;p&gt;Go to &lt;strong&gt;Connections&lt;/strong&gt; → &lt;strong&gt;Data sources&lt;/strong&gt;. Prometheus should be configured and ready. Create a dashboard and run a query to confirm.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Apply manifest&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; grafana-application.yaml

&lt;span class="c"&gt;# 2. Wait for sync (check Argo CD UI or: argocd app get grafana)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Port-forward and get password&lt;/span&gt;
kubectl port-forward svc/grafana &lt;span class="nt"&gt;-n&lt;/span&gt; grafana 3000:80 &amp;amp;
kubectl get secret grafana &lt;span class="nt"&gt;-n&lt;/span&gt; grafana &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.admin-password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and log in with &lt;code&gt;admin&lt;/code&gt; / the password.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Application stuck in Syncing or OutOfSync&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check Argo CD logs and the Application status. Ensure the Grafana Helm repo is reachable and &lt;code&gt;storageClassName: ebs-sc&lt;/code&gt; exists in your cluster. If using a different storage class, update the manifest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Prometheus datasource connection failed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Verify Prometheus is running: &lt;code&gt;kubectl get svc -n kube-prometheus-stack&lt;/code&gt; (or your Prometheus namespace). Update the &lt;code&gt;url&lt;/code&gt; in the manifest to match your Prometheus service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; Argo CD ingress stuck or inaccessible&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; See &lt;a href="https://dev.to/jajera/argocd-ingress-stuck-find-fix"&gt;Argo CD Ingress Stuck – Find &amp;amp; Fix&lt;/a&gt; for troubleshooting steps.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grafana Helm Chart&lt;/strong&gt;: &lt;a href="https://github.com/grafana/helm-charts" rel="noopener noreferrer"&gt;https://github.com/grafana/helm-charts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana Documentation&lt;/strong&gt;: &lt;a href="https://grafana.com/docs/grafana/latest/" rel="noopener noreferrer"&gt;https://grafana.com/docs/grafana/latest/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt;: &lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;https://argo-cd.readthedocs.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>grafana</category>
      <category>eks</category>
      <category>argocd</category>
      <category>prometheus</category>
    </item>
  </channel>
</rss>
