<?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: Tandap Noel Bansikah</title>
    <description>The latest articles on DEV Community by Tandap Noel Bansikah (@bansikah).</description>
    <link>https://dev.to/bansikah</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%2F986646%2F61247089-46d8-4739-be45-f47d34d83d9f.jpeg</url>
      <title>DEV Community: Tandap Noel Bansikah</title>
      <link>https://dev.to/bansikah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bansikah"/>
    <language>en</language>
    <item>
      <title>Kyverno Without the Noise: Practical Kubernetes Policies for Security &amp; Best Practices</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Fri, 23 Jan 2026 17:12:41 +0000</pubDate>
      <link>https://dev.to/bansikah/kyverno-without-the-noise-practical-kubernetes-policies-for-security-best-practices-21i8</link>
      <guid>https://dev.to/bansikah/kyverno-without-the-noise-practical-kubernetes-policies-for-security-best-practices-21i8</guid>
      <description>&lt;p&gt;How to introduce guardrails in Kubernetes using native YAML — without slowing teams down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Kubernetes gives teams a lot of power. Too much power, sometimes. One &lt;code&gt;privileged: true&lt;/code&gt; or missing resource limit can quietly turn into a security incident or a production outage.&lt;/p&gt;

&lt;p&gt;That's where Kyverno comes in.&lt;/p&gt;

&lt;p&gt;This article is not a full Kyverno manual. Instead, it's a guided introduction: why Kyverno exists, how it fits into Kubernetes, and a few concrete security-focused examples to get you started. If it clicks, you can go deeper using the official docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Kyverno — and why should you care?
&lt;/h2&gt;

&lt;p&gt;Kyverno is a Kubernetes-native policy engine. It lets you define rules for your cluster using plain YAML, the same format you already use for Deployments, Services, and Ingresses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No new language&lt;/li&gt;
&lt;li&gt;No external policy service
&lt;/li&gt;
&lt;li&gt;No complex tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Kubernetes understands YAML, Kyverno understands your policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Kubernetes needs policy enforcement
&lt;/h2&gt;

&lt;p&gt;Kubernetes validates syntax, not intent.&lt;/p&gt;

&lt;p&gt;That means Kubernetes will happily accept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pods running as root&lt;/li&gt;
&lt;li&gt;Containers without memory limits&lt;/li&gt;
&lt;li&gt;Images pulled from untrusted registries&lt;/li&gt;
&lt;li&gt;Inconsistent labels and annotations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real clusters — especially multi-team ones — these are not edge cases. They're normal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policies turn expectations into guarantees.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Kyverno fits in the Kubernetes lifecycle
&lt;/h2&gt;

&lt;p&gt;Kyverno runs as an admission controller. Every time someone tries to create, update, or delete a resource, Kyverno gets a chance to inspect it.&lt;/p&gt;

&lt;p&gt;It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; it (allow or deny)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutate&lt;/strong&gt; it (fix or enhance it)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt; other resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit&lt;/strong&gt; it (report without blocking)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Admission flow (simplified)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi1820xv6osboy7ipofs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgi1820xv6osboy7ipofs.png" alt="Admission flow" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happens before anything is stored in etcd.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policies as YAML — and why that matters
&lt;/h2&gt;

&lt;p&gt;Most policy engines introduce a new DSL. Kyverno doesn't.&lt;/p&gt;

&lt;p&gt;A Kyverno policy looks like a Kubernetes resource:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apiVersion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kind&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spec&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers can read policies&lt;/li&gt;
&lt;li&gt;Platform teams can review them&lt;/li&gt;
&lt;li&gt;GitOps tools can manage them&lt;/li&gt;
&lt;li&gt;Policies feel like part of the cluster — not an add-on&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The main types of Kyverno policies (quick tour)
&lt;/h2&gt;

&lt;p&gt;Kyverno supports multiple policy types for different use cases:&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Policy Types
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ClusterPolicy&lt;/strong&gt; - Cluster-wide policies (used in our examples)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate, mutate, generate resources&lt;/li&gt;
&lt;li&gt;Verify image signatures and attestations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Policy&lt;/strong&gt; - Namespace-scoped policies&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same capabilities as ClusterPolicy but limited to specific namespaces&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Specialized Policy Types (v1.14+)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ValidatingPolicy&lt;/strong&gt; - Pure validation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate Kubernetes resources or JSON payloads&lt;/li&gt;
&lt;li&gt;Lightweight alternative to full ClusterPolicy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ImageValidatingPolicy&lt;/strong&gt; - Image security&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify container image signatures and attestations&lt;/li&gt;
&lt;li&gt;Focused on supply chain security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MutatingPolicy&lt;/strong&gt; (v1.15+) - Resource modification&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutate new or existing resources&lt;/li&gt;
&lt;li&gt;Dedicated mutation capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GeneratingPolicy&lt;/strong&gt; (v1.15+) - Resource creation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create or clone resources based on flexible triggers&lt;/li&gt;
&lt;li&gt;Advanced resource generation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Utility Types
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CleanupPolicy&lt;/strong&gt; - Resource cleanup&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delete matching resources based on a schedule&lt;/li&gt;
&lt;li&gt;Automated cluster maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DeletingPolicy&lt;/strong&gt; (v1.15+) - Scheduled deletion&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delete matching resources based on a schedule&lt;/li&gt;
&lt;li&gt;Enhanced cleanup capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CEL Libraries&lt;/strong&gt; - Extended functions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extended CEL functions for complex policy logic&lt;/li&gt;
&lt;li&gt;Advanced policy features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this guide, we'll focus on &lt;strong&gt;ClusterPolicy&lt;/strong&gt; - the most versatile option that covers validation, mutation, and generation in one resource type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You'll need a Kubernetes cluster to follow along. Choose one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minikube:&lt;/strong&gt; &lt;code&gt;minikube start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KodeKloud Playground:&lt;/strong&gt; &lt;a href="https://kodekloud.com/public-playgrounds" rel="noopener noreferrer"&gt;kodecloud kubernetes playground&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kind, k3s, or any Kubernetes cluster&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install Kyverno
&lt;/h3&gt;

&lt;p&gt;Multiple installation options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Direct YAML (recommended for testing)&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 &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kyverno/kyverno/releases/latest/download/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Helm&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;helm repo add kyverno https://kyverno.github.io/kyverno/
helm &lt;span class="nb"&gt;install &lt;/span&gt;kyverno kyverno/kyverno &lt;span class="nt"&gt;-n&lt;/span&gt; kyverno &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 3: Kustomize&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 apply &lt;span class="nt"&gt;-k&lt;/span&gt; https://github.com/kyverno/kyverno/config/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get the Demo Repository
&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/bansikah22/kyverno-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;kyverno-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo time: 3 practical Kyverno scenarios
&lt;/h2&gt;

&lt;p&gt;All examples below are ready-to-run and intentionally minimal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Block privileged containers
&lt;/h3&gt;

&lt;p&gt;Running privileged containers is one of the fastest ways to break your security model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy: deny privileged Pods&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;kyverno.io/v1&lt;/span&gt;        &lt;span class="c1"&gt;# Kyverno API version&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;ClusterPolicy&lt;/span&gt;              &lt;span class="c1"&gt;# Policy applies cluster-wide&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-privileged-containers&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;validationFailureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt; &lt;span class="c1"&gt;# Block resources that fail validation&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-privileged&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;resources&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="c1"&gt;# Apply this rule to Pod resources&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Privileged&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;containers&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;allowed"&lt;/span&gt;
        &lt;span class="na"&gt;pattern&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;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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;privileged&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;# Require privileged to be false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; the Pod never reaches the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Enforce resource requests and limits
&lt;/h3&gt;

&lt;p&gt;Missing resource limits are a classic reliability problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy: require CPU &amp;amp; memory limits&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;kyverno.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;ClusterPolicy&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;require-resource-limits&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;validationFailureAction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enforce&lt;/span&gt; &lt;span class="c1"&gt;# Block pods without limits&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-limits&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;resources&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="c1"&gt;# Target Pod resources&lt;/span&gt;
      &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;limits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required"&lt;/span&gt;
        &lt;span class="na"&gt;pattern&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;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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?*"&lt;/span&gt;      &lt;span class="c1"&gt;# Require any CPU limit value&lt;/span&gt;
                    &lt;span class="na"&gt;memory&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="c1"&gt;# Require any memory limit value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns best practices into cluster rules, not documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Let Kyverno fix things automatically
&lt;/h3&gt;

&lt;p&gt;Blocking is good. Helping is better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy: inject a default securityContext&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;kyverno.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;ClusterPolicy&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;add-security-context&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-non-root&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;resources&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="c1"&gt;# Apply to Pod resources&lt;/span&gt;
      &lt;span class="na"&gt;mutate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                    &lt;span class="c1"&gt;# Modify the resource&lt;/span&gt;
        &lt;span class="na"&gt;patchStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="c1"&gt;# Merge this config into the Pod&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;runAsNonRoot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Automatically add non-root security&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers don't change anything — the cluster becomes safer by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy Scope: ClusterPolicy vs Policy
&lt;/h2&gt;

&lt;p&gt;Kyverno offers two policy scopes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ClusterPolicy&lt;/strong&gt; (used in our examples):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applies cluster-wide to all namespaces&lt;/li&gt;
&lt;li&gt;Perfect for organization-wide security standards&lt;/li&gt;
&lt;li&gt;Requires cluster-admin permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Policy&lt;/strong&gt; (namespace-scoped):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applies only to resources in a specific namespace&lt;/li&gt;
&lt;li&gt;Ideal for team-specific rules&lt;/li&gt;
&lt;li&gt;Can be managed by namespace admins
&lt;/li&gt;
&lt;/ul&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;kyverno.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;Policy&lt;/span&gt;                    &lt;span class="c1"&gt;# Namespace-scoped policy&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;team-specific-limits&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;development&lt;/span&gt;        &lt;span class="c1"&gt;# Only applies to 'development' namespace&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Same rules as ClusterPolicy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose based on your governance model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit vs Enforce: adopting Kyverno safely
&lt;/h2&gt;

&lt;p&gt;Kyverno doesn't force you to go all-in.&lt;/p&gt;

&lt;p&gt;You can start in &lt;strong&gt;Audit&lt;/strong&gt; mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Policies report violations&lt;/li&gt;
&lt;li&gt;No workloads are blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then move to &lt;strong&gt;Enforce&lt;/strong&gt; when teams are ready.&lt;/p&gt;

&lt;p&gt;This makes Kyverno realistic for existing clusters.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Kyverno shines — and when it doesn't
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kyverno is excellent when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You want Kubernetes-native policies&lt;/li&gt;
&lt;li&gt;You value readability and YAML&lt;/li&gt;
&lt;li&gt;You enforce platform and security standards&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Kyverno is not:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A runtime security tool&lt;/li&gt;
&lt;li&gt;A replacement for vulnerability scanners&lt;/li&gt;
&lt;li&gt;A general-purpose policy engine outside Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing this avoids misuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If Kyverno makes sense so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explore more policy examples in the &lt;a href="https://kyverno.io/policies/" rel="noopener noreferrer"&gt;official policy library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Look into image verification and supply-chain security&lt;/li&gt;
&lt;li&gt;Try combining Kyverno with GitOps workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kyverno doesn't try to do everything — it does policy enforcement the Kubernetes way.&lt;/p&gt;

&lt;p&gt;And that's exactly why it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-on Testing
&lt;/h2&gt;

&lt;p&gt;Now let's test everything together:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Apply the policies
&lt;/h3&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; policies/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test policy enforcement
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Try the bad pod (should be rejected):&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 apply &lt;span class="nt"&gt;-f&lt;/span&gt; workloads/bad-pod.yaml
&lt;span class="c"&gt;# Expected: Error - privileged containers not allowed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b4a06hg82ekrf99dyuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b4a06hg82ekrf99dyuh.png" alt="bad pod" width="800" height="200"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Deploy the good pod (should succeed):&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 apply &lt;span class="nt"&gt;-f&lt;/span&gt; workloads/good-pod.yaml
&lt;span class="c"&gt;# Expected: Pod created successfully&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft88zmle1k0d1xps53jho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft88zmle1k0d1xps53jho.png" alt="good pod" width="800" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy production workload:&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 apply &lt;span class="nt"&gt;-f&lt;/span&gt; workloads/kubesrv-deployment.yaml
&lt;span class="c"&gt;# Expected: Deployment and service created&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Verify the results
&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;# Check pods&lt;/span&gt;
kubectl get pods

&lt;span class="c"&gt;# Test the service (use port 8080 to avoid permission issues)&lt;/span&gt;
kubectl port-forward svc/kubesrv-service 8080:8080

&lt;span class="c"&gt;# In another terminal, test with curl&lt;/span&gt;
curl http://localhost:8080

&lt;span class="c"&gt;## or visit browser with http://localhost:8080 and you will se &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp52tffeon5ca7wjj5x8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp52tffeon5ca7wjj5x8n.png" alt="kubesrv" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Check policy reports
&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;# View policy violations&lt;/span&gt;
kubectl get cpol
kubectl describe cpol disallow-privileged-containers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Kyverno brings policy enforcement to Kubernetes without the complexity. By using familiar YAML syntax and integrating natively with the Kubernetes API, it makes security and governance accessible to every team member.&lt;/p&gt;

&lt;p&gt;The key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start small&lt;/strong&gt;: Begin with a few critical policies in Audit mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use ClusterPolicy&lt;/strong&gt; for organization-wide standards, &lt;strong&gt;Policy&lt;/strong&gt; for team-specific rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage mutation&lt;/strong&gt; to help teams rather than just block them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combine with GitOps&lt;/strong&gt; for policy-as-code workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kyverno doesn't try to solve every problem — it solves policy enforcement the Kubernetes way. And that focus is exactly what makes it powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kyverno.io/docs/" rel="noopener noreferrer"&gt;Official Kyverno Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kyverno.io/policies/" rel="noopener noreferrer"&gt;Kyverno Policy Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kyverno/kyverno" rel="noopener noreferrer"&gt;Kyverno GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/" rel="noopener noreferrer"&gt;Kubernetes Admission Controllers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bansikah22/kyverno-demo" rel="noopener noreferrer"&gt;Demo Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bansikah22/kubesrv" rel="noopener noreferrer"&gt;KubeServ Test Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Ready to implement Kyverno in your cluster? Start with the demo repository and gradually expand your policy coverage. Remember: the best policy is one that helps teams succeed while keeping the cluster secure.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kyverno</category>
      <category>kubernetes</category>
      <category>security</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Terraform State Management with Amazon S3</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Wed, 07 Jan 2026 07:02:37 +0000</pubDate>
      <link>https://dev.to/bansikah/terraform-state-management-with-amazon-s3-1478</link>
      <guid>https://dev.to/bansikah/terraform-state-management-with-amazon-s3-1478</guid>
      <description>&lt;p&gt;In our previous article the &lt;a href="https://dev.to/bansikah/amazon-eks-series-part-1-introduction-to-eks-4m3p"&gt;AWS EKS series&lt;/a&gt;, i mentioned that we will later have an article about managing terraform state. Managing Terraform state correctly is one of the most important skills for anyone using Terraform in real-world AWS environments.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore how to store your Terraform state in an Amazon S3 remote backend, why this matters, essential best practices (including state locking), and a hands-on example you can follow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide uses official &lt;a href="https://developer.hashicorp.com/terraform/language/settings/backends/s3" rel="noopener noreferrer"&gt;Terraform documentation&lt;/a&gt; as the source of truth.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Terraform State Matters
&lt;/h2&gt;

&lt;p&gt;Terraform uses a state file (&lt;code&gt;terraform.tfstate&lt;/code&gt;) to track what infrastructure it manages. This file contains important mappings between your configuration and real resources — so Terraform can make safe and predictable changes in future runs.&lt;/p&gt;

&lt;p&gt;By default, Terraform stores state locally, which works for experimentation but is &lt;strong&gt;unsafe for collaboration, automation, and production use&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use a Remote Backend like S3?
&lt;/h2&gt;

&lt;p&gt;Using a remote backend like Amazon S3 provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized state accessible by all team members&lt;/li&gt;
&lt;li&gt;Collaboration support&lt;/li&gt;
&lt;li&gt;Encryption and access control via IAM&lt;/li&gt;
&lt;li&gt;Versioning for recovery&lt;/li&gt;
&lt;li&gt;Support for state locking to avoid conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform's official documentation confirms that S3 is a supported backend type that can store state and support locking — and it's one of the most popular and reliable backends used in AWS environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  State Locking — Preventing Conflicts
&lt;/h2&gt;

&lt;p&gt;Terraform needs to prevent two people from writing changes to the same state file at the same time — otherwise state could become corrupted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern Locking (S3 Native)
&lt;/h3&gt;

&lt;p&gt;Terraform now supports &lt;strong&gt;native state locking in S3&lt;/strong&gt; through a lockfile mechanism. This means Terraform will create a &lt;code&gt;.tflock&lt;/code&gt; file in the bucket alongside your state file, preventing concurrent runs.&lt;/p&gt;

&lt;p&gt;This is done using the optional argument &lt;code&gt;use_lockfile&lt;/code&gt; in your backend config.&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state-bucket2z"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"envs/prod/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;use_lockfile&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use_lockfile = true&lt;/code&gt; enables S3-native locking&lt;/li&gt;
&lt;li&gt;S3 creates a &lt;code&gt;.tflock&lt;/code&gt; file during a Terraform run&lt;/li&gt;
&lt;li&gt;Other processes must wait until the lock is released&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removes the need for a separate DynamoDB table for state locking — an approach that is officially deprecated and will be removed in future Terraform releases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; DynamoDB locking is still supported for backward compatibility, but Terraform now prefers S3 native locking for new configurations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI installed and configured&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform installed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permissions&lt;/strong&gt; to create S3 buckets and apply Terraform changes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1 — Create an S3 Bucket for Terraform State
&lt;/h2&gt;

&lt;p&gt;Terraform cannot create the backend bucket for you, so you must create it first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api create-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-terraform-state-bucket2z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;my-terraform-state-bucket2z&lt;/code&gt; with a globally unique name.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2 — Enable Bucket Versioning (Best Practice)
&lt;/h2&gt;

&lt;p&gt;Versioning helps you recover from accidental deletions or corruption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-terraform-state-bucket2z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3 — Enable Encryption on the Bucket
&lt;/h2&gt;

&lt;p&gt;Encrypting state at rest is essential for security:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api put-bucket-encryption &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-terraform-state-bucket2z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--server-side-encryption-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
      }
    }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting: AccessDenied Error
&lt;/h3&gt;

&lt;p&gt;If you encounter an &lt;code&gt;AccessDenied&lt;/code&gt; error when running the encryption command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;An error occurred (AccessDenied) when calling the PutBucketEncryption operation: Access Denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes and fixes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Insufficient IAM Permissions&lt;/strong&gt; — Your IAM user/role needs &lt;code&gt;s3:PutEncryptionConfiguration&lt;/code&gt; permission&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket doesn't exist&lt;/strong&gt; — Verify the bucket name is correct:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws s3api head-bucket &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-terraform-state-bucket2z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wrong AWS credentials&lt;/strong&gt; — Verify your identity:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4 — Configure Terraform Backend
&lt;/h2&gt;

&lt;p&gt;In your Terraform directory, define the backend like this:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state-bucket2z"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;use_lockfile&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The S3 bucket you created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to store the state file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;use_lockfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables native S3 state locking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Demo: Using S3 Remote State
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create a Terraform Project
&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;mkdir &lt;/span&gt;terraform-s3-state-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-s3-state-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add Terraform Code
&lt;/h3&gt;

&lt;p&gt;Create a file &lt;code&gt;main.tf&lt;/code&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&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;"~&amp;gt; 5.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state-bucket2z"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&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;use_lockfile&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&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_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tf-s3-backend-demo-example"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Initialize the Backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform will initialize and automatically configure the backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Apply the Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your state file is now stored in S3, and state locking is enabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6hi1ssta0d6cxqtcptl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6hi1ssta0d6cxqtcptl1.png" alt="s3 remote" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Terraform Backend Commands
&lt;/h2&gt;

&lt;p&gt;Here are some helpful Terraform commands for managing remote state:&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrate Local State to Remote
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init &lt;span class="nt"&gt;-migrate-state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reconfigure Backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init &lt;span class="nt"&gt;-reconfigure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List State Resources
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform state list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspect a Resource
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform state show aws_s3_bucket.example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Additional Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Store State Remotely
&lt;/h3&gt;

&lt;p&gt;Use S3 or Terraform Cloud — never store state locally in team environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Edit or Delete State Manually
&lt;/h3&gt;

&lt;p&gt;Manual changes corrupt state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolate State by Environment
&lt;/h3&gt;

&lt;p&gt;Use different keys for different environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;envs/dev/terraform.tfstate
envs/staging/terraform.tfstate
envs/prod/terraform.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Regular Backups
&lt;/h3&gt;

&lt;p&gt;Enable S3 versioning so you can roll back state accidentally overwritten or deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock State on All Writes
&lt;/h3&gt;

&lt;p&gt;Enable &lt;code&gt;use_lockfile = true&lt;/code&gt;. It ensures only one Terraform process can modify state at a time, protecting against conflicts and corruption.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleanup: Removing Demo Resources Safely
&lt;/h2&gt;

&lt;p&gt;After completing the demo, it's important to clean up resources to avoid unnecessary AWS costs and to keep your environment tidy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Always destroy Terraform-managed infrastructure before deleting the remote state backend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1: Destroy Terraform Resources
&lt;/h3&gt;

&lt;p&gt;From your Terraform project directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fg2acmumnmr610pt7t4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fg2acmumnmr610pt7t4.png" alt="tf destroy" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the state file from S3&lt;/li&gt;
&lt;li&gt;Identify all managed resources&lt;/li&gt;
&lt;li&gt;Safely delete them from AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confirm when prompted.&lt;/p&gt;

&lt;p&gt;At this point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All demo infrastructure is removed&lt;/li&gt;
&lt;li&gt;The Terraform state file still exists in S3 (this is expected)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Verify State File in S3 (Optional)
&lt;/h3&gt;

&lt;p&gt;You can confirm the state file still exists:&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;ls &lt;/span&gt;s3://my-terraform-state-bucket2z/demo/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform keeps the state file so you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A record of managed infrastructure&lt;/li&gt;
&lt;li&gt;Historical versions (if versioning is enabled)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Delete the Terraform State File (Optional)
&lt;/h3&gt;

&lt;p&gt;If this was only a demo and you no longer need the state file, you may delete it after destroying resources.&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;rm &lt;/span&gt;s3://my-terraform-state-bucket2z/demo/terraform.tfstate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Never delete a state file while infrastructure still exists — this will orphan resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: (Optional) Disable or Remove Lock Files
&lt;/h3&gt;

&lt;p&gt;If you used S3 native locking, Terraform automatically removes the &lt;code&gt;.tflock&lt;/code&gt; file after operations complete.&lt;/p&gt;

&lt;p&gt;You normally do not need to manually remove lock files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Delete the S3 Backend Bucket (Optional)
&lt;/h3&gt;

&lt;p&gt;If the S3 bucket was created only for this demo, and you no longer need it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Empty the Bucket:&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;aws s3 &lt;span class="nb"&gt;rm &lt;/span&gt;s3://my-terraform-state-bucket2z &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Delete the Bucket:&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;aws s3api delete-bucket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-terraform-state-bucket2z &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleanup Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always run &lt;code&gt;terraform destroy&lt;/code&gt; first&lt;/li&gt;
&lt;li&gt;Never delete state files before destroying resources&lt;/li&gt;
&lt;li&gt;Keep state buckets for long-term projects&lt;/li&gt;
&lt;li&gt;Enable versioning for recovery&lt;/li&gt;
&lt;li&gt;Restrict access using IAM&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Terraform state is foundational to infrastructure automation.&lt;/p&gt;

&lt;p&gt;Storing it in a remote S3 backend with native state locking enabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensures collaboration&lt;/li&gt;
&lt;li&gt;Prevents concurrent modifications&lt;/li&gt;
&lt;li&gt;Allows controlled recovery&lt;/li&gt;
&lt;li&gt;Supports CI/CD workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;S3 remote state with native locking is now a best practice recommended by Terraform — and removes the need for a DynamoDB lock table in most cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/language/settings/backends/s3" rel="noopener noreferrer"&gt;Terraform S3 Backend (Official Docs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/language/state/locking" rel="noopener noreferrer"&gt;Terraform State Locking (Official Docs)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Terraform state cleanup is just as important as provisioning.&lt;/p&gt;

&lt;p&gt;A safe cleanup process ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No orphaned resources&lt;/li&gt;
&lt;li&gt;No unexpected AWS charges&lt;/li&gt;
&lt;li&gt;Clean environments for future work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When using remote state: &lt;strong&gt;Terraform is always the source of truth&lt;/strong&gt; — let it manage the lifecycle from start to finish.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>EKS vs ECS: A Comprehensive Comparison</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Mon, 05 Jan 2026 22:06:18 +0000</pubDate>
      <link>https://dev.to/bansikah/eks-vs-ecs-a-comprehensive-comparison-351i</link>
      <guid>https://dev.to/bansikah/eks-vs-ecs-a-comprehensive-comparison-351i</guid>
      <description>&lt;h2&gt;
  
  
  What Problem Are We Solving?
&lt;/h2&gt;

&lt;p&gt;Imagine you're running a restaurant. You have multiple chefs (containers) cooking different dishes (applications). Now, who decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which chef works on which dish?&lt;/li&gt;
&lt;li&gt;What happens if a chef gets sick mid-service?&lt;/li&gt;
&lt;li&gt;How many chefs do you need during the lunch rush vs. a quiet Tuesday afternoon?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the software world, &lt;strong&gt;containers&lt;/strong&gt; are like those chefs—small, independent workers running your applications. And just like a restaurant needs a manager to coordinate everything, containers need a &lt;strong&gt;container orchestrator&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Container Orchestration?
&lt;/h2&gt;

&lt;p&gt;Container orchestration is simply &lt;strong&gt;automated container management&lt;/strong&gt;. Think of it as an intelligent manager that:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deciding where each container should run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding more containers when traffic spikes, removing them when it's quiet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-healing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically restarting containers that crash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Load balancing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spreading incoming traffic evenly across containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Without orchestration, you'd have to manually start, stop, monitor, and manage potentially hundreds of containers. That's not practical—or fun.&lt;/p&gt;




&lt;h2&gt;
  
  
  Meet the Two AWS Solutions
&lt;/h2&gt;

&lt;p&gt;AWS offers two main container orchestration services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ECS&lt;/strong&gt; (Elastic Container Service) — AWS's own solution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EKS&lt;/strong&gt; (Elastic Kubernetes Service) — Managed Kubernetes on AWS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's break down each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Amazon ECS: The Simple Choice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is ECS?
&lt;/h3&gt;

&lt;p&gt;ECS is AWS's &lt;strong&gt;homegrown container orchestration service&lt;/strong&gt;. It's designed to be straightforward and works seamlessly with other AWS services.&lt;/p&gt;

&lt;p&gt;Think of ECS as hiring a property management company that only works with buildings in one city (AWS). They know that city inside and out, making everything smooth—but you can't use them if you ever move to another city.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does ECS Work?
&lt;/h3&gt;

&lt;p&gt;ECS has two main parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Control Plane&lt;/strong&gt; — The "brain" that decides what runs where (AWS manages this for you—for free!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute&lt;/strong&gt; — The actual servers that run your containers&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Where Do Your Containers Actually Run?
&lt;/h3&gt;

&lt;p&gt;You have two options:&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: EC2 Launch Type
&lt;/h4&gt;

&lt;p&gt;You provide your own virtual servers (EC2 instances), and ECS places containers on them.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;More control over the underlying servers&lt;/li&gt;
&lt;li&gt;Can be cheaper for steady, predictable workloads&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;You manage the servers (updates, security patches, capacity)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Option 2: Fargate Launch Type
&lt;/h4&gt;

&lt;p&gt;AWS handles everything. You just say "run this container" and AWS figures out the rest.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Zero server management&lt;/li&gt;
&lt;li&gt;Pay only for what you use, down to the second&lt;/li&gt;
&lt;li&gt;Perfect for variable workloads&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Can be more expensive for always-on workloads&lt;/li&gt;
&lt;li&gt;Less control over the underlying infrastructure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Amazon EKS: The Kubernetes Option
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is EKS?
&lt;/h3&gt;

&lt;p&gt;EKS is &lt;strong&gt;Kubernetes managed by AWS&lt;/strong&gt;. Kubernetes (often called "K8s") is the industry-standard container orchestrator, originally created by Google.&lt;/p&gt;

&lt;p&gt;Think of EKS as hiring an internationally certified property management company. They follow global standards, so if you ever move to another city (another cloud provider), your processes stay the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Does Kubernetes Exist?
&lt;/h3&gt;

&lt;p&gt;Running Kubernetes yourself is hard. You'd need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up and secure master nodes (the control plane)&lt;/li&gt;
&lt;li&gt;Install and configure multiple complex components&lt;/li&gt;
&lt;li&gt;Handle upgrades, backups, and high availability&lt;/li&gt;
&lt;li&gt;Keep everything secure and patched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EKS takes care of all the hard parts. AWS manages the control plane, and you just focus on running your applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Do Your Containers Run on EKS?
&lt;/h3&gt;

&lt;p&gt;Just like ECS, you need compute resources. EKS gives you three options:&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: Self-Managed Nodes
&lt;/h4&gt;

&lt;p&gt;You create and manage your own EC2 instances, installing all the necessary Kubernetes components yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Teams needing maximum customization or specific compliance requirements.&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 2: Managed Node Groups (Recommended)
&lt;/h4&gt;

&lt;p&gt;AWS creates and manages EC2 instances for you. Updates and patches happen automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Most production workloads. You get convenience without giving up too much control.&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 3: Fargate
&lt;/h4&gt;

&lt;p&gt;Completely serverless. AWS handles everything—you don't even think about servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Variable workloads or teams that want zero infrastructure management.&lt;/p&gt;




&lt;h2&gt;
  
  
  ECS vs EKS: The Honest Comparison
&lt;/h2&gt;

&lt;p&gt;Let's compare these services across what actually matters:&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Curve
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Easier&lt;/strong&gt; — Simple concepts, AWS-native terminology&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Harder&lt;/strong&gt; — Must learn Kubernetes concepts plus AWS specifics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be productive in days&lt;/td&gt;
&lt;td&gt;May take weeks to become comfortable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; If you're new to containers, ECS will get you running faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Control plane is &lt;strong&gt;free&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Control plane is &lt;strong&gt;paid&lt;/strong&gt; (per cluster per hour)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pay only for compute (EC2 or Fargate)&lt;/td&gt;
&lt;td&gt;Pay for control plane + compute&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For current EKS pricing, refer to the &lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;official AWS EKS Pricing page&lt;/a&gt;. Pricing varies based on Kubernetes version support tier (standard vs. extended).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; ECS is cheaper, especially for smaller deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vendor Lock-in
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS-only — your knowledge and configurations don't transfer&lt;/td&gt;
&lt;td&gt;Kubernetes is universal — skills transfer to any cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;If AWS changes ECS, you adapt&lt;/td&gt;
&lt;td&gt;Kubernetes is open-source with a stable API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; EKS gives you more flexibility and portability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ecosystem and Tooling
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Limited to AWS tools&lt;/td&gt;
&lt;td&gt;Massive ecosystem: Helm, ArgoCD, Istio, Prometheus, and hundreds more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smaller community&lt;/td&gt;
&lt;td&gt;Huge global community with endless resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; EKS has a richer ecosystem and more community support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Complexity
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simpler to operate day-to-day&lt;/td&gt;
&lt;td&gt;More moving parts to understand and manage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Less configuration needed&lt;/td&gt;
&lt;td&gt;More powerful, but more configuration required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; ECS is easier to operate; EKS offers more power at the cost of complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making Your Decision: A Simple Framework
&lt;/h2&gt;

&lt;p&gt;Ask yourself these questions:&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose ECS if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're new to containers and want to start quickly
&lt;/li&gt;
&lt;li&gt;Your team is small and doesn't have Kubernetes experience
&lt;/li&gt;
&lt;li&gt;You're fully committed to AWS with no plans to use other clouds
&lt;/li&gt;
&lt;li&gt;You want the simplest possible setup
&lt;/li&gt;
&lt;li&gt;Cost optimization is a top priority
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose EKS if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your team already knows Kubernetes
&lt;/li&gt;
&lt;li&gt;You might use multiple clouds in the future (AWS, Azure, GCP)
&lt;/li&gt;
&lt;li&gt;You need advanced features like service mesh, custom controllers, or complex networking
&lt;/li&gt;
&lt;li&gt;You want to use popular K8s tools (Helm, ArgoCD, etc.)
&lt;/li&gt;
&lt;li&gt;You're running large-scale, complex microservices
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Startup Building Their First App
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; A 5-person startup launching their first web application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation:&lt;/strong&gt; &lt;strong&gt;ECS with Fargate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why?&lt;/em&gt; They need to move fast, have limited DevOps experience, and shouldn't spend time managing infrastructure. ECS is simpler, and Fargate means zero server management.&lt;/p&gt;




&lt;h3&gt;
  
  
  Scenario 2: Enterprise Migrating from On-Premises
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; A large company moving their existing Kubernetes workloads from on-premises data centers to AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation:&lt;/strong&gt; &lt;strong&gt;EKS with Managed Node Groups&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why?&lt;/em&gt; They already have Kubernetes expertise and existing configurations. EKS lets them reuse their knowledge and tooling with minimal changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Scenario 3: Company with Multi-Cloud Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Situation:&lt;/strong&gt; A company that uses AWS primarily but also deploys to Azure for specific clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation:&lt;/strong&gt; &lt;strong&gt;EKS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why?&lt;/em&gt; Kubernetes skills and configurations work across clouds. They can use similar practices on AWS (EKS) and Azure (AKS).&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;How hard is it to learn?&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Moderate to Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What does the control plane cost?&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Paid (per cluster per hour)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can I use it on other clouds?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Kubernetes is universal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How big is the community?&lt;/td&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;Massive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for beginners?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not ideal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for Kubernetes users?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless option?&lt;/td&gt;
&lt;td&gt;Fargate&lt;/td&gt;
&lt;td&gt;Fargate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There's no universally "better" choice between ECS and EKS—only what's better &lt;strong&gt;for your situation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECS&lt;/strong&gt; is like an automatic car: easy to learn, gets you where you need to go, and requires less attention. Perfect for most drivers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EKS&lt;/strong&gt; is like a manual car: more control, more powerful in the right hands, but requires more skill to operate well. Preferred by enthusiasts and professionals.&lt;/p&gt;

&lt;p&gt;Start with what matches your team's skills and your project's needs. You can always evolve your choice as your requirements grow.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;Amazon EKS Pricing&lt;/a&gt; - Official AWS pricing page for EKS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/ecs/pricing/" rel="noopener noreferrer"&gt;Amazon ECS Pricing&lt;/a&gt; - Official AWS pricing page for ECS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/fargate/pricing/" rel="noopener noreferrer"&gt;AWS Fargate Pricing&lt;/a&gt; - Official AWS pricing page for Fargate&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/" rel="noopener noreferrer"&gt;Amazon EKS Documentation&lt;/a&gt; - Official EKS documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/ecs/" rel="noopener noreferrer"&gt;Amazon ECS Documentation&lt;/a&gt; - Official ECS documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/" rel="noopener noreferrer"&gt;Kubernetes Documentation&lt;/a&gt; - Official Kubernetes documentation&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>cloudnative</category>
      <category>devops</category>
    </item>
    <item>
      <title>Deploying Applications on Amazon ECS: A Practical Guide</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Fri, 19 Dec 2025 08:47:58 +0000</pubDate>
      <link>https://dev.to/bansikah/deploying-applications-on-amazon-ecs-a-practical-guide-5gm4</link>
      <guid>https://dev.to/bansikah/deploying-applications-on-amazon-ecs-a-practical-guide-5gm4</guid>
      <description>&lt;h1&gt;
  
  
  Deploying Applications on Amazon ECS: A Practical Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I will walk you through deploying a containerized application on &lt;strong&gt;Amazon ECS (Elastic Container Service)&lt;/strong&gt; using AWS Fargate. We will use a real full-stack application called &lt;strong&gt;CloudNotes&lt;/strong&gt; that has already been developed and dockerized.&lt;/p&gt;

&lt;p&gt;Our focus will be entirely on ECS concepts, the deployment process, and understanding the Terraform infrastructure code. By the end of this article, you will have a clear understanding of how ECS works and how to deploy your own containerized applications on AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/bansikah22/cloudnotes" rel="noopener noreferrer"&gt;github.com/bansikah22/cloudnotes&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Amazon ECS?
&lt;/h2&gt;

&lt;p&gt;Amazon ECS (Elastic Container Service) is a fully managed container orchestration service provided by AWS. It manages how your Docker containers are defined, deployed, scaled, and maintained.&lt;/p&gt;

&lt;p&gt;ECS handles the complexity of running containers at scale while integrating deeply with other AWS services like IAM, CloudWatch, VPC, and Application Load Balancers.&lt;/p&gt;




&lt;h2&gt;
  
  
  ECS Core Concepts
&lt;/h2&gt;

&lt;p&gt;Understanding ECS requires grasping its main objects and how they relate to each other:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6c7zeo82ffwwr2e3d9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6c7zeo82ffwwr2e3d9g.png" alt="Core concept" width="606" height="756"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cluster
&lt;/h3&gt;

&lt;p&gt;An ECS Cluster is a logical grouping of tasks or services. It serves as the foundation where your container workloads run. You can think of it as a namespace that organizes your services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Definition
&lt;/h3&gt;

&lt;p&gt;A Task Definition is a blueprint that describes how containers should be deployed. It is unique to ECS and defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much CPU and memory a container will use&lt;/li&gt;
&lt;li&gt;The container image to pull&lt;/li&gt;
&lt;li&gt;Port mappings&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Volumes and other runtime settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, a Task Definition contains the same configuration you would normally place in a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: Port mapping in ECS (especially with &lt;code&gt;awsvpc&lt;/code&gt; network mode on Fargate) must map the same port to the same port (e.g., &lt;code&gt;3000:3000&lt;/code&gt;). Mappings like &lt;code&gt;3000:3001&lt;/code&gt; will not work because ECS does not allow arbitrary host-to-container remapping in this mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task
&lt;/h3&gt;

&lt;p&gt;A Task is an instance of a Task Definition. It represents a running copy of your application. The Task contains one or more running containers defined by the Task Definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;An ECS Service ensures that a specified number of Tasks are running at all times. It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic restart of tasks if they crash or exit&lt;/li&gt;
&lt;li&gt;Rescheduling of tasks on healthy instances if an EC2 instance fails (in EC2 mode)&lt;/li&gt;
&lt;li&gt;Integration with load balancers to distribute traffic across tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ECS Load Balancers
&lt;/h3&gt;

&lt;p&gt;An Application Load Balancer (ALB) can be assigned to an ECS Service. It routes external traffic to tasks through a Target Group. The ECS service automatically registers and deregisters tasks to the Target Group as they start and stop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Launch Types: Fargate vs EC2
&lt;/h3&gt;

&lt;p&gt;ECS offers two launch types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Launch Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fargate&lt;/td&gt;
&lt;td&gt;Serverless - AWS manages the underlying infrastructure&lt;/td&gt;
&lt;td&gt;Most workloads, simpler operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EC2&lt;/td&gt;
&lt;td&gt;You manage the EC2 instances that host your containers&lt;/td&gt;
&lt;td&gt;GPU workloads, specific instance requirements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For this guide, we use Fargate because it eliminates server management, you pay only for resources used, and it handles infrastructure scaling automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CloudNotes Application
&lt;/h2&gt;

&lt;p&gt;The application we are deploying is a full-stack ToDo Notes application. It has already been developed and dockerized, so our focus remains on the ECS deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: React, Vite, TypeScript, served via Nginx&lt;/li&gt;
&lt;li&gt;Backend: Node.js, Express, TypeScript&lt;/li&gt;
&lt;li&gt;Infrastructure: Terraform, AWS ECS Fargate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Docker images are available on Docker Hub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bansikah/cloudnotes-frontend:latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bansikah/cloudnotes-backend:latest&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To follow along with this guide, clone the repository and explore the infrastructure code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/bansikah22/cloudnotes.git
&lt;span class="nb"&gt;cd &lt;/span&gt;cloudnotes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

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

&lt;ol&gt;
&lt;li&gt;AWS CLI installed and configured with appropriate credentials (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Terraform v1.0+ installed&lt;/li&gt;
&lt;li&gt;Docker installed (for local testing if needed)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Understanding the Terraform Infrastructure
&lt;/h2&gt;

&lt;p&gt;The infrastructure code is located in the &lt;code&gt;infrastructure/&lt;/code&gt; directory. Let me walk you through the key components.&lt;/p&gt;

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

&lt;p&gt;We use the terraform-aws-modules VPC module to create a network with public and private subnets across two availability zones:&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;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&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;"~&amp;gt; 5.0"&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;"cloudnotes-vpc"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;

  &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&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_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.101.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.102.0/24"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&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;single_nat_gateway&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;p&gt;The ALB sits in public subnets while ECS tasks run in private subnets, accessing the internet through the NAT Gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  ECS Cluster
&lt;/h3&gt;

&lt;p&gt;The cluster is simply a logical grouping:&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_ecs_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"cloudnotes"&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;"cloudnotes-cluster"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Task Definitions
&lt;/h3&gt;

&lt;p&gt;Here is the backend task definition showing how we define container configurations:&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_ecs_task_definition"&lt;/span&gt; &lt;span class="s2"&gt;"backend"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;family&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudnotes-backend"&lt;/span&gt;
  &lt;span class="nx"&gt;requires_compatibilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;network_mode&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;
  &lt;span class="nx"&gt;cpu&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;backend_cpu&lt;/span&gt;
  &lt;span class="nx"&gt;memory&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;backend_memory&lt;/span&gt;
  &lt;span class="nx"&gt;execution_role_arn&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;ecs_task_execution_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;task_role_arn&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;ecs_task_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;container_definitions&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;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend_container&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;The container definition includes the image, port mappings, environment variables, and logging configuration:&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;backend_container&lt;/span&gt; &lt;span class="err"&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;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"backend"&lt;/span&gt;
    &lt;span class="nx"&gt;image&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;backend_image&lt;/span&gt;
    &lt;span class="nx"&gt;essential&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;portMappings&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;containerPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
        &lt;span class="nx"&gt;hostPort&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;environment&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;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NODE_ENV"&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;logConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logDriver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awslogs"&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-group"&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-region"&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;aws_region&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-stream-prefix"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"backend"&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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ECS Services
&lt;/h3&gt;

&lt;p&gt;The service ensures our tasks are always running and connects them to the load balancer:&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_ecs_service"&lt;/span&gt; &lt;span class="s2"&gt;"backend"&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;"cloudnotes-backend"&lt;/span&gt;
  &lt;span class="nx"&gt;cluster&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudnotes&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;task_definition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;desired_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;desired_count&lt;/span&gt;
  &lt;span class="nx"&gt;launch_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;

  &lt;span class="nx"&gt;network_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;subnets&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&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;backend&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;assign_public_ip&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;load_balancer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
    &lt;span class="nx"&gt;container_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"backend"&lt;/span&gt;
    &lt;span class="nx"&gt;container_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;health_check_grace_period_seconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&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_lb_listener_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_path&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;
  
  
  Application Load Balancer with Path-Based Routing
&lt;/h3&gt;

&lt;p&gt;The ALB distributes traffic between frontend and backend services using path-based routing:&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_lb"&lt;/span&gt; &lt;span class="s2"&gt;"cloudnotes"&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;"cloudnotes-alb"&lt;/span&gt;
  &lt;span class="nx"&gt;internal&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;load_balancer_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"application"&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;subnets&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets&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_lb_listener"&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;load_balancer_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudnotes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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;"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;default_action&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="s2"&gt;"forward"&lt;/span&gt;
    &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontend&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="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_listener_rule"&lt;/span&gt; &lt;span class="s2"&gt;"api_path"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;listener_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;priority&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

  &lt;span class="nx"&gt;action&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="s2"&gt;"forward"&lt;/span&gt;
    &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backend&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="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;path_pattern&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/api/*"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This routes all &lt;code&gt;/api/*&lt;/code&gt; requests to the backend service and everything else to the frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Groups
&lt;/h3&gt;

&lt;p&gt;The security groups control network access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALB security group allows inbound HTTP/HTTPS from the internet&lt;/li&gt;
&lt;li&gt;Frontend security group allows traffic only from the ALB on port 80&lt;/li&gt;
&lt;li&gt;Backend security group allows traffic only from the ALB on port 5000
&lt;/li&gt;
&lt;/ul&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_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"backend"&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;"cloudnotes-backend-sg"&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;"Allow only ALB to access backend"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&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;5000&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;5000&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="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="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&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;0&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;0&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;"-1"&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="s2"&gt;"0.0.0.0/0"&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;h3&gt;
  
  
  IAM Roles
&lt;/h3&gt;

&lt;p&gt;Two IAM roles are created:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Task Execution Role&lt;/strong&gt;: Allows ECS to pull images and write logs to CloudWatch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Role&lt;/strong&gt;: Grants permissions to the running application itself&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Deploying the Infrastructure
&lt;/h2&gt;

&lt;p&gt;Navigate to the infrastructure directory 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;cd &lt;/span&gt;infrastructure

&lt;span class="c"&gt;# Initialize Terraform&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# Validate the configuration&lt;/span&gt;
terraform validate

&lt;span class="c"&gt;# Review the execution plan&lt;/span&gt;
terraform plan

&lt;span class="c"&gt;# Deploy the infrastructure&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted to confirm the deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Observing the Deployed Application
&lt;/h2&gt;

&lt;p&gt;Once the deployment completes, Terraform outputs the ALB DNS name. You can access your application using this URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  ECS Cluster
&lt;/h3&gt;

&lt;p&gt;The cluster dashboard shows your services and their status:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioqftjov3osk0hlxnomj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioqftjov3osk0hlxnomj.png" alt="Cluster" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Services
&lt;/h3&gt;

&lt;p&gt;Both frontend and backend services are running with their desired task counts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qrfyl3v7m5e7abtvdyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qrfyl3v7m5e7abtvdyi.png" alt="ECS Services" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Definition
&lt;/h3&gt;

&lt;p&gt;The task definition view shows container configurations, resource allocations, and logging settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2ra8k585wcdlujuezne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2ra8k585wcdlujuezne.png" alt="Task Definition" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Running Application
&lt;/h3&gt;

&lt;p&gt;Access the application via the ALB DNS name to see CloudNotes in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr17ao6tt4lumz4gibdsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr17ao6tt4lumz4gibdsi.png" alt="Deployed App" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture follows a standard pattern for containerized applications on AWS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users access the application through the Application Load Balancer&lt;/li&gt;
&lt;li&gt;The ALB sits in public subnets and routes traffic based on URL paths&lt;/li&gt;
&lt;li&gt;Frontend requests go to the frontend ECS service&lt;/li&gt;
&lt;li&gt;API requests (&lt;code&gt;/api/*&lt;/code&gt;) go to the backend ECS service&lt;/li&gt;
&lt;li&gt;Both services run as Fargate tasks in private subnets&lt;/li&gt;
&lt;li&gt;Tasks pull images from Docker Hub and send logs to CloudWatch&lt;/li&gt;
&lt;li&gt;A NAT Gateway enables outbound internet access for the private subnets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;To avoid ongoing AWS charges, destroy the infrastructure when you are done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Amazon ECS is and its core components (Cluster, Service, Task Definition, Task)&lt;/li&gt;
&lt;li&gt;How traffic flows from users through the ALB to ECS services&lt;/li&gt;
&lt;li&gt;The Terraform code that provisions the entire infrastructure&lt;/li&gt;
&lt;li&gt;How to deploy and observe a running application on ECS Fargate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ECS provides a straightforward way to run containers on AWS without managing the underlying infrastructure. With Fargate, you can focus entirely on your application while AWS handles server provisioning, scaling, and maintenance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Next
&lt;/h2&gt;

&lt;p&gt;In upcoming articles, we will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS vs EKS: A detailed comparison to help you choose the right service&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/bansikah22/cloudnotes" rel="noopener noreferrer"&gt;github.com/bansikah22/cloudnotes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS ECS Documentation: &lt;a href="https://docs.aws.amazon.com/ecs/" rel="noopener noreferrer"&gt;docs.aws.amazon.com/ecs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Terraform AWS Provider: &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" rel="noopener noreferrer"&gt;registry.terraform.io/providers/hashicorp/aws&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you have questions or feedback, feel free to drop a comment below. If you found this helpful, consider following for more AWS and DevOps content.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ecs</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Amazon EKS Series - Part 5: GitOps with Argo CD on EKS</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Thu, 18 Dec 2025 14:54:00 +0000</pubDate>
      <link>https://dev.to/bansikah/amazon-eks-series-part-5-gitops-with-argo-cd-on-eks-4992</link>
      <guid>https://dev.to/bansikah/amazon-eks-series-part-5-gitops-with-argo-cd-on-eks-4992</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome to the final part of the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/bansikah/amazon-eks-series-part-4-deploying-applications-on-amazon-eks-9f6"&gt;Part 4&lt;/a&gt;, we deployed applications manually using &lt;code&gt;kubectl&lt;/code&gt; and explored Helm for package management. While these approaches work, they have limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual processes&lt;/strong&gt; are error-prone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No audit trail&lt;/strong&gt; of who deployed what and when&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drift&lt;/strong&gt; can occur between what's in Git and what's running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollbacks&lt;/strong&gt; require remembering previous configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we'll solve these problems by implementing &lt;strong&gt;GitOps&lt;/strong&gt; with &lt;strong&gt;Argo CD&lt;/strong&gt;. You'll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand GitOps principles and benefits&lt;/li&gt;
&lt;li&gt;Install and configure Argo CD on EKS&lt;/li&gt;
&lt;li&gt;Deploy a real application (CloudNotes) using GitOps&lt;/li&gt;
&lt;li&gt;Enable automated sync and self-healing&lt;/li&gt;
&lt;li&gt;Observe deployments through the Argo CD UI&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 from Part 3 running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; configured and working&lt;/li&gt;
&lt;li&gt;Basic understanding of Kubernetes Deployments and Services
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify cluster connection&lt;/span&gt;
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmsm6cd7bknnqh1cbqo7.png" alt="Get Nodes" width="800" height="190"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What Is GitOps?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitOps&lt;/strong&gt; is an operational framework that uses Git as the single source of truth for declarative infrastructure and applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Idea
&lt;/h3&gt;

&lt;p&gt;Instead of running commands to deploy applications:&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;# Traditional approach&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With GitOps, you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commit&lt;/strong&gt; your desired state to Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An agent&lt;/strong&gt; (like Argo CD) watches the repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically&lt;/strong&gt; applies changes to the cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  GitOps Principles
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Declarative&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The entire system is described declaratively in Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Versioned&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All changes are tracked with Git history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approved changes are applied automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-healing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The system corrects drift from the desired state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Desired State vs Actual State
&lt;/h3&gt;

&lt;p&gt;GitOps continuously compares:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desired State&lt;/strong&gt;: What's defined in your Git repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual State&lt;/strong&gt;: What's currently running in the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If they differ, GitOps reconciles by applying the desired state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐         ┌─────────────────┐
│   Git Repo      │         │   Kubernetes    │
│  (Desired State)│◄───────►│  (Actual State) │
└─────────────────┘  Sync   └─────────────────┘
         │                           │
         └───────── Argo CD ─────────┘
              (Reconciliation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why GitOps Matters for EKS
&lt;/h2&gt;

&lt;p&gt;GitOps is particularly powerful for Amazon EKS environments:&lt;/p&gt;

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

&lt;p&gt;Every deployment is a Git commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who made the change?&lt;/li&gt;
&lt;li&gt;When was it made?&lt;/li&gt;
&lt;li&gt;What exactly changed?&lt;/li&gt;
&lt;li&gt;Why? (commit message)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is invaluable for compliance and debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Easy Rollbacks
&lt;/h3&gt;

&lt;p&gt;Made a bad deployment? Simply revert the Git commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git revert HEAD
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Argo CD automatically rolls back the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Team Collaboration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use pull requests for deployment reviews&lt;/li&gt;
&lt;li&gt;Enforce approval workflows&lt;/li&gt;
&lt;li&gt;No need to share cluster credentials widely&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Reduced Human Error
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No more typos in &lt;code&gt;kubectl&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;No forgotten flags or wrong contexts&lt;/li&gt;
&lt;li&gt;Consistent deployments every time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Disaster Recovery
&lt;/h3&gt;

&lt;p&gt;Your entire cluster state is in Git. If you lose the cluster:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new EKS cluster&lt;/li&gt;
&lt;li&gt;Install Argo CD&lt;/li&gt;
&lt;li&gt;Point to your Git repos&lt;/li&gt;
&lt;li&gt;Everything redeploys automatically&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Introducing Argo CD
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Argo CD&lt;/strong&gt; is a declarative, GitOps continuous delivery tool for Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Argo CD Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Runs inside your cluster&lt;/strong&gt; as a set of Kubernetes controllers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watches Git repositories&lt;/strong&gt; for changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compares&lt;/strong&gt; desired state (Git) with actual state (cluster)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syncs&lt;/strong&gt; the cluster to match Git (manually or automatically)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports&lt;/strong&gt; status through UI, CLI, and API&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Argo CD vs kubectl/Helm
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;kubectl/Helm&lt;/th&gt;
&lt;th&gt;Argo CD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deployment trigger&lt;/td&gt;
&lt;td&gt;Manual command&lt;/td&gt;
&lt;td&gt;Git commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State tracking&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Continuous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drift detection&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Git revert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail&lt;/td&gt;
&lt;td&gt;External logging&lt;/td&gt;
&lt;td&gt;Git history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;None (CLI only)&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Argo CD Architecture (Simplified)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────┐
│                    Argo CD                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │ API Server  │  │ Repo Server │  │ Application │  │
│  │  (UI/CLI)   │  │ (Git sync)  │  │ Controller  │  │
│  └─────────────┘  └─────────────┘  └─────────────┘  │
└──────────────────────────────────────────────────────┘
         │                  │                │
         ▼                  ▼                ▼
    ┌─────────┐       ┌──────────┐    ┌───────────────┐
    │ Web UI  │       │   Git    │    │  Kubernetes   │
    │   CLI   │       │  Repos   │    │    Cluster    │
    └─────────┘       └──────────┘    └───────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Server&lt;/strong&gt;: Provides the web UI and CLI interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo Server&lt;/strong&gt;: Clones and caches Git repositories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Controller&lt;/strong&gt;: Monitors applications and syncs state&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Installing Argo CD on EKS
&lt;/h2&gt;

&lt;p&gt;Let's install Argo CD on our EKS cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create Namespace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Install Argo CD
&lt;/h3&gt;

&lt;p&gt;We'll use the official installation manifests:&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;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Argo CD API Server&lt;/li&gt;
&lt;li&gt;Argo CD Repo Server&lt;/li&gt;
&lt;li&gt;Argo CD Application Controller&lt;/li&gt;
&lt;li&gt;Redis (for caching)&lt;/li&gt;
&lt;li&gt;Dex (for authentication)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Wait for Pods to be Ready
&lt;/h3&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; argocd &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglepqian4tnggdx8mb9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglepqian4tnggdx8mb9l.png" alt="Get argocd pods" width="800" height="190"&gt;&lt;/a&gt;&lt;br&gt;
Wait until all pods show &lt;code&gt;Running&lt;/code&gt; status (1-2 minutes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                               READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                    1/1     Running   0          60s
argocd-applicationset-controller-5f7b9d6b4-xxxxx   1/1     Running   0          60s
argocd-dex-server-6dcf4f8d4b-xxxxx                 1/1     Running   0          60s
argocd-notifications-controller-5c8d7b4c6d-xxxxx   1/1     Running   0          60s
argocd-redis-6976fc7dfc-xxxxx                      1/1     Running   0          60s
argocd-repo-server-7b8d7c4f5-xxxxx                 1/1     Running   0          60s
argocd-server-5f8d7c4f5b-xxxxx                     1/1     Running   0          60s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;Ctrl+C&lt;/code&gt; to stop watching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Expose the Argo CD Server
&lt;/h3&gt;

&lt;p&gt;For this tutorial, we'll use port-forwarding. In production, you'd use an Ingress or LoadBalancer.&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/argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd 8080:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnuv10l2itwsnozqinuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnuv10l2itwsnozqinuu.png" alt="Port forward" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep this terminal open. The Argo CD UI is now available at: &lt;code&gt;https://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Get the Initial Admin Password
&lt;/h3&gt;

&lt;p&gt;Argo CD generates a random password for the &lt;code&gt;admin&lt;/code&gt; user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd admin initial-password &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or using kubectl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd get secret argocd-initial-admin-secret &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.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;p&gt;Save this password - you'll need it to log in.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1ii887vsapdlns1wmwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1ii887vsapdlns1wmwx.png" alt="Login" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 6: Access the UI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;https://localhost:8080&lt;/code&gt; in your browser&lt;/li&gt;
&lt;li&gt;Accept the self-signed certificate warning&lt;/li&gt;
&lt;li&gt;Log in with:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username&lt;/strong&gt;: &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password&lt;/strong&gt;: (from Step 5)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see the Argo CD dashboard - currently empty since we haven't deployed any applications yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2mxublhsa4n757mzjkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2mxublhsa4n757mzjkr.png" alt="Argo Dashboard" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 7: Install Argo CD CLI (Optional but Recommended)
&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;# Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 555 argocd-linux-amd64 /usr/local/bin/argocd
&lt;span class="nb"&gt;rm &lt;/span&gt;argocd-linux-amd64

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;argocd

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
argocd version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Login via CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd login localhost:8080 &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;your-password&amp;gt; &lt;span class="nt"&gt;--insecure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The CloudNotes Application
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we'll deploy &lt;strong&gt;CloudNotes&lt;/strong&gt; - a full-stack ToDo Notes application.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/bansikah22/cloudnotes" rel="noopener noreferrer"&gt;github.com/bansikah22/cloudnotes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React + Vite + TypeScript (served by Nginx)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js + Express + TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images&lt;/strong&gt;: Available on Docker Hub&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why CloudNotes?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real application&lt;/strong&gt; - Not just a demo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple services&lt;/strong&gt; - Frontend and Backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Already containerized&lt;/strong&gt; - Docker images ready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demonstrates GitOps&lt;/strong&gt; - Multiple manifests to sync&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Preparing Kubernetes Manifests for GitOps
&lt;/h2&gt;

&lt;p&gt;For GitOps, we need Kubernetes manifests stored in a Git repository. We'll create them in our EKS series repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;argocd/
├── README.md
├── applications/
│   └── cloudnotes.yaml          # Argo CD Application resource
└── cloudnotes/
    ├── namespace.yaml           # Kubernetes namespace
    ├── nginx-configmap.yaml     # Nginx config for API proxy
    ├── backend-deployment.yaml  # Backend deployment
    ├── backend-service.yaml     # Backend service (ClusterIP)
    ├── frontend-deployment.yaml # Frontend deployment
    └── frontend-service.yaml    # Frontend service (LoadBalancer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation keeps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD configuration&lt;/strong&gt; in &lt;code&gt;applications/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application manifests&lt;/strong&gt; in &lt;code&gt;cloudnotes/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Writing the Kubernetes Manifests
&lt;/h2&gt;

&lt;p&gt;Let's create the manifests for CloudNotes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Namespace
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/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;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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;managed-by&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;h3&gt;
  
  
  Backend Deployment
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/backend-deployment.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;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;cloudnotes-backend&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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;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;cloudnotes&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&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;cloudnotes&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&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;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;backend&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;bansikah/cloudnotes-backend:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
        &lt;span class="na"&gt;env&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;NODE_ENV&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&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;PORT&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/health&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backend Service
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/backend-service.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;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;Service&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;cloudnotes-backend&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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The backend uses &lt;code&gt;ClusterIP&lt;/code&gt; (internal only). The frontend's Nginx will proxy API requests to this service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Nginx ConfigMap (API Proxy)
&lt;/h3&gt;

&lt;p&gt;The frontend is a static React app served by Nginx. When your browser makes API calls to &lt;code&gt;/api/*&lt;/code&gt;, we need Nginx to proxy those requests to the backend service.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/nginx-configmap.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;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-config&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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default.conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;server {&lt;/span&gt;
        &lt;span class="s"&gt;listen 80;&lt;/span&gt;

        &lt;span class="s"&gt;# Serve static frontend files&lt;/span&gt;
        &lt;span class="s"&gt;location / {&lt;/span&gt;
            &lt;span class="s"&gt;root /usr/share/nginx/html;&lt;/span&gt;
            &lt;span class="s"&gt;index index.html index.htm;&lt;/span&gt;
            &lt;span class="s"&gt;try_files $uri $uri/ /index.html;&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;

        &lt;span class="s"&gt;# Proxy API requests to backend service&lt;/span&gt;
        &lt;span class="s"&gt;location /api/ {&lt;/span&gt;
            &lt;span class="s"&gt;proxy_pass http://cloudnotes-backend:5000/api/;&lt;/span&gt;
            &lt;span class="s"&gt;proxy_http_version 1.1;&lt;/span&gt;
            &lt;span class="s"&gt;proxy_set_header Host $host;&lt;/span&gt;
            &lt;span class="s"&gt;proxy_set_header X-Real-IP $remote_addr;&lt;/span&gt;
            &lt;span class="s"&gt;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;/span&gt;
            &lt;span class="s"&gt;proxy_set_header X-Forwarded-Proto $scheme;&lt;/span&gt;
        &lt;span class="s"&gt;}&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;Why is this needed?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The frontend runs in your &lt;strong&gt;browser&lt;/strong&gt; (external)&lt;/li&gt;
&lt;li&gt;The backend is &lt;strong&gt;ClusterIP&lt;/strong&gt; (internal only)&lt;/li&gt;
&lt;li&gt;Without the proxy, API calls from the browser would fail&lt;/li&gt;
&lt;li&gt;Nginx acts as a reverse proxy, forwarding &lt;code&gt;/api/*&lt;/code&gt; requests to the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontend Deployment
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/frontend-deployment.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;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;cloudnotes-frontend&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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;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;cloudnotes&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&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;cloudnotes&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&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;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;frontend&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;bansikah/cloudnotes-frontend:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;64Mi"&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&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;nginx-config&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/http.d/default.conf&lt;/span&gt;
          &lt;span class="na"&gt;subPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default.conf&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;nginx-config&lt;/span&gt;
        &lt;span class="na"&gt;configMap&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;nginx-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code&gt;volumeMounts&lt;/code&gt; section mounts our custom Nginx config, enabling the API proxy to the backend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Frontend Service
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/cloudnotes/frontend-service.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;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;Service&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;cloudnotes-frontend&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;cloudnotes&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;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloudnotes&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating an Argo CD Application
&lt;/h2&gt;

&lt;p&gt;Now we create the Argo CD Application resource that tells Argo CD what to deploy and where.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Application CRD
&lt;/h3&gt;

&lt;p&gt;An Argo CD &lt;strong&gt;Application&lt;/strong&gt; is a Custom Resource that defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: Where to get the manifests (Git repo, path, branch)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination&lt;/strong&gt;: Where to deploy (cluster, namespace)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync Policy&lt;/strong&gt;: How to handle updates (manual/automatic)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Application Manifest
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;argocd/applications/cloudnotes.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;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;cloudnotes&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;finalizers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;resources-finalizer.argocd.argoproj.io&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;

  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/bansikah22/eks-at-scale.git&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;master&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd/cloudnotes&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;cloudnotes&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;allowEmpty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;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;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PrunePropagationPolicy=foreground&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PruneLast=true&lt;/span&gt;
    &lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;backoff&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
        &lt;span class="na"&gt;factor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="na"&gt;maxDuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the Application
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&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;code&gt;metadata.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the application in Argo CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;metadata.namespace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be &lt;code&gt;argocd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spec.project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Argo CD project (use &lt;code&gt;default&lt;/code&gt; for now)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source.repoURL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Git repository URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source.targetRevision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Branch, tag, or commit to track&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source.path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to manifests within the repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;destination.server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target cluster (in-cluster uses this URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;destination.namespace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target namespace for resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;syncPolicy.automated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable automatic sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;syncPolicy.automated.prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete resources removed from Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;syncPolicy.automated.selfHeal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Revert manual changes to cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Deploying via GitOps
&lt;/h2&gt;

&lt;p&gt;Now let's see GitOps in action!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Commit and Push the Manifests
&lt;/h3&gt;

&lt;p&gt;First, ensure all your manifests are committed to Git:&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;cd&lt;/span&gt; /path/to/eks-at-scale
git add argocd/
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add CloudNotes GitOps manifests"&lt;/span&gt;
git push origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Apply the Argo CD Application
&lt;/h3&gt;



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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application.argoproj.io/cloudnotes created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Watch Argo CD Sync
&lt;/h3&gt;

&lt;p&gt;Check the application status:&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; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME         SYNC STATUS   HEALTH STATUS
cloudnotes   Synced        Healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the Argo CD CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd app get cloudnotes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjsgr8ww7bsso2mbfg61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjsgr8ww7bsso2mbfg61.png" alt="sync" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Observe in the UI
&lt;/h3&gt;

&lt;p&gt;Open the Argo CD UI (&lt;code&gt;https://localhost:8080&lt;/code&gt;) and you'll see:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;cloudnotes&lt;/strong&gt; application card&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync Status&lt;/strong&gt;: Synced (green)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health Status&lt;/strong&gt;: Healthy (green heart)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Click on the application to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All Kubernetes resources created&lt;/li&gt;
&lt;li&gt;Resource relationships (Deployment -&amp;gt; ReplicaSet -&amp;gt; Pods)&lt;/li&gt;
&lt;li&gt;Real-time sync status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsdpp2lnhkfgkhqhrnlb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsdpp2lnhkfgkhqhrnlb.png" alt=" " width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Verify the Deployment
&lt;/h3&gt;

&lt;p&gt;Check that CloudNotes 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;&lt;span class="c"&gt;# Check pods&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes

&lt;span class="c"&gt;# Check services&lt;/span&gt;
kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9ce7h4v73hammj2xhup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9ce7h4v73hammj2xhup.png" alt="Get pods" width="800" height="279"&gt;&lt;/a&gt;&lt;br&gt;
Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                   READY   STATUS    RESTARTS   AGE
cloudnotes-backend-xxxxx-xxxxx         1/1     Running   0          2m
cloudnotes-backend-xxxxx-xxxxx         1/1     Running   0          2m
cloudnotes-frontend-xxxxx-xxxxx        1/1     Running   0          2m
cloudnotes-frontend-xxxxx-xxxxx        1/1     Running   0          2m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Access the Application
&lt;/h3&gt;

&lt;p&gt;Get the LoadBalancer URL:&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 svc cloudnotes-frontend &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the EXTERNAL-IP:&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;LB_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get svc cloudnotes-frontend &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes &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.loadBalancer.ingress[0].hostname}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CloudNotes URL: http://&lt;/span&gt;&lt;span class="nv"&gt;$LB_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the URL in your browser to see the CloudNotes application!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbp0fklx04tays6x4qpf.png" alt="application" width="800" height="454"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Automated Sync and Self-Healing
&lt;/h2&gt;

&lt;p&gt;This is where GitOps truly shines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated Sync in Action
&lt;/h3&gt;

&lt;p&gt;Let's make a change to our manifests and watch Argo CD automatically sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Update the replica count:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;argocd/cloudnotes/frontend-deployment.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;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;3&lt;/span&gt;  &lt;span class="c1"&gt;# Changed from 2 to 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Commit and push:&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;git add argocd/cloudnotes/frontend-deployment.yaml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Scale frontend to 3 replicas"&lt;/span&gt;
git push origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Watch Argo CD sync:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Within 3 minutes (default sync interval), Argo CD will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect the Git change&lt;/li&gt;
&lt;li&gt;Compare with cluster state&lt;/li&gt;
&lt;li&gt;Apply the new configuration
&lt;/li&gt;
&lt;/ol&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; cloudnotes &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frontend &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a third frontend pod being created automatically!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fhrfyejvkif11ujxkyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fhrfyejvkif11ujxkyv.png" alt="Syncfrontendpodterminal" width="800" height="79"&gt;&lt;/a&gt;&lt;br&gt;
and on ArgoCD dashboard you will see &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihaq5a5aw9tifmmsf6xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihaq5a5aw9tifmmsf6xc.png" alt="Argo sync" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Self-Healing in Action
&lt;/h3&gt;

&lt;p&gt;Self-healing reverts unauthorized changes made directly to the cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Manually delete a pod:&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 pod &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frontend &lt;span class="nt"&gt;--wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Watch Argo CD restore it:&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 pods &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frontend &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within seconds, Argo CD detects the drift and recreates the pod to match the desired state (3 replicas).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Try scaling manually:&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 scale deployment cloudnotes-frontend &lt;span class="nt"&gt;-n&lt;/span&gt; cloudnotes &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch as Argo CD reverts this change back to 3 replicas (as defined in Git).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accidental deletions&lt;/strong&gt; are automatically recovered&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unauthorized changes&lt;/strong&gt; are reverted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster state&lt;/strong&gt; always matches Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No manual intervention&lt;/strong&gt; required&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Observing the Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Argo CD UI Features
&lt;/h3&gt;

&lt;p&gt;The Argo CD web interface provides:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application View:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual resource tree showing all Kubernetes objects&lt;/li&gt;
&lt;li&gt;Health status for each resource&lt;/li&gt;
&lt;li&gt;Sync status (in sync, out of sync, unknown)&lt;/li&gt;
&lt;li&gt;Recent sync history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resource Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click any resource to see its YAML&lt;/li&gt;
&lt;li&gt;View events and logs&lt;/li&gt;
&lt;li&gt;See relationships between resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sync Operations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual sync button&lt;/li&gt;
&lt;li&gt;Sync with prune option&lt;/li&gt;
&lt;li&gt;Rollback to previous versions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Useful CLI Commands
&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;# List all applications&lt;/span&gt;
argocd app list

&lt;span class="c"&gt;# Get detailed application info&lt;/span&gt;
argocd app get cloudnotes

&lt;span class="c"&gt;# View application history&lt;/span&gt;
argocd app &lt;span class="nb"&gt;history &lt;/span&gt;cloudnotes

&lt;span class="c"&gt;# Manually trigger sync&lt;/span&gt;
argocd app &lt;span class="nb"&gt;sync &lt;/span&gt;cloudnotes

&lt;span class="c"&gt;# View application logs&lt;/span&gt;
argocd app logs cloudnotes

&lt;span class="c"&gt;# Rollback to previous version&lt;/span&gt;
argocd app rollback cloudnotes 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Health Status
&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;# Quick status check&lt;/span&gt;
kubectl get applications &lt;span class="nt"&gt;-n&lt;/span&gt; argocd

&lt;span class="c"&gt;# Detailed health&lt;/span&gt;
argocd app get cloudnotes &lt;span class="nt"&gt;--show-operation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Health statuses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Healthy&lt;/strong&gt;: All resources are healthy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressing&lt;/strong&gt;: Resources are being updated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degraded&lt;/strong&gt;: Some resources have issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suspended&lt;/strong&gt;: Application is paused&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing&lt;/strong&gt;: Resources don't exist yet&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  GitOps Workflow Summary
&lt;/h2&gt;

&lt;p&gt;Here's the complete GitOps workflow we've implemented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer                    Git                      Argo CD                  EKS Cluster
    │                         │                          │                          │
    │  1. Edit manifests      │                          │                          │
    │─────────────────────────►                          │                          │
    │                         │                          │                          │
    │  2. git commit &amp;amp; push   │                          │                          │
    │─────────────────────────►                          │                          │
    │                         │                          │                          │
    │                         │  3. Detect changes       │                          │
    │                         │◄─────────────────────────│                          │
    │                         │                          │                          │
    │                         │                          │  4. Apply manifests      │
    │                         │                          │─────────────────────────►│
    │                         │                          │                          │
    │                         │                          │  5. Report status        │
    │                         │                          │◄─────────────────────────│
    │                         │                          │                          │
    │  6. View in UI/CLI      │                          │                          │
    │◄─────────────────────────────────────────────────────                         │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;When you're done experimenting:&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete the Argo CD Application
&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;# This will delete all CloudNotes resources&lt;/span&gt;
argocd app delete cloudnotes &lt;span class="nt"&gt;--cascade&lt;/span&gt;

&lt;span class="c"&gt;# Or using kubectl&lt;/span&gt;
kubectl delete application cloudnotes &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uninstall Argo CD (Optional)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl delete namespace argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete the EKS Cluster
&lt;/h3&gt;

&lt;p&gt;If you're done with the entire series:&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;cd &lt;/span&gt;eks-terraform
terraform destroy &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitOps Fundamentals&lt;/strong&gt; - Git as the single source of truth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why GitOps Matters&lt;/strong&gt; - Auditability, rollbacks, collaboration, disaster recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD&lt;/strong&gt; - A powerful GitOps tool for Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installation&lt;/strong&gt; - Setting up Argo CD on EKS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Deployment&lt;/strong&gt; - Deploying CloudNotes via GitOps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Sync&lt;/strong&gt; - Changes in Git automatically applied&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Healing&lt;/strong&gt; - Drift detection and automatic correction&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git is the source of truth&lt;/strong&gt; - All desired state lives in version control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argo CD automates deployments&lt;/strong&gt; - No more manual &lt;code&gt;kubectl apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changes are auditable&lt;/strong&gt; - Every deployment is a Git commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-healing ensures consistency&lt;/strong&gt; - The cluster always matches Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollbacks are simple&lt;/strong&gt; - Just revert the Git commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team collaboration improves&lt;/strong&gt; - Use PRs for deployment reviews&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Series Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations! You've completed the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;Over five articles, you've learned:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Key Skills&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Introduction to EKS&lt;/td&gt;
&lt;td&gt;Understanding managed Kubernetes, worker nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;EKS Architecture&lt;/td&gt;
&lt;td&gt;Control plane, networking, IAM integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Provisioning with Terraform&lt;/td&gt;
&lt;td&gt;Infrastructure as Code, VPC, EKS modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deploying Applications&lt;/td&gt;
&lt;td&gt;Deployments, Services, LoadBalancers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;GitOps with Argo CD&lt;/td&gt;
&lt;td&gt;Declarative deployments, automated sync, self-healing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You now have a complete, production-ready workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Infrastructure (Terraform) → Cluster (EKS) → Applications (GitOps/Argo CD)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;Here are some areas to explore further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Management&lt;/strong&gt; - HashiCorp Vault, AWS Secrets Manager, Sealed Secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Integration&lt;/strong&gt; - GitHub Actions, Jenkins, GitLab CI with Argo CD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Cluster&lt;/strong&gt; - Argo CD managing multiple EKS clusters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Argo CD&lt;/strong&gt; - ApplicationSets, App of Apps pattern&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; - Prometheus, Grafana, CloudWatch Container Insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Mesh&lt;/strong&gt; - Istio, AWS App Mesh for advanced networking&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;Argo CD Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opengitops.dev/" rel="noopener noreferrer"&gt;GitOps Principles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bansikah22/cloudnotes" rel="noopener noreferrer"&gt;CloudNotes Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/" rel="noopener noreferrer"&gt;Amazon EKS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/" rel="noopener noreferrer"&gt;Kubernetes Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Thank You!
&lt;/h2&gt;

&lt;p&gt;Thank you for following along with this series! &lt;/p&gt;

&lt;p&gt;If you found it helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the &lt;a href="https://github.com/bansikah22/eks-at-scale" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Share with others learning Kubernetes&lt;/li&gt;
&lt;li&gt;Drop your questions and feedback in the comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy deploying!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This concludes the Amazon EKS at Scale series. Stay tuned for more DevOps and cloud-native content!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>gitops</category>
      <category>argocd</category>
    </item>
    <item>
      <title>Amazon EKS Series - Part 4: Deploying Applications on Amazon EKS</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Wed, 17 Dec 2025 15:50:54 +0000</pubDate>
      <link>https://dev.to/bansikah/amazon-eks-series-part-4-deploying-applications-on-amazon-eks-9f6</link>
      <guid>https://dev.to/bansikah/amazon-eks-series-part-4-deploying-applications-on-amazon-eks-9f6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome back to the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/bansikah/amazon-eks-series-part-3-provisioning-an-eks-cluster-with-terraform-413c"&gt;Part 3&lt;/a&gt;, we provisioned a production-ready EKS cluster using Terraform and community modules. Now it's time to put that cluster to work!&lt;/p&gt;

&lt;p&gt;In this article, you'll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy a simple application using Kubernetes&lt;/li&gt;
&lt;li&gt;Understand Deployments and Pods&lt;/li&gt;
&lt;li&gt;Expose applications with Services&lt;/li&gt;
&lt;li&gt;Use AWS Load Balancers with EKS&lt;/li&gt;
&lt;li&gt;Get started with Helm for package management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; Ensure your EKS cluster from Part 3 is running and &lt;code&gt;kubectl&lt;/code&gt; is configured.&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;# Verify cluster connection&lt;/span&gt;
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your worker nodes in &lt;code&gt;Ready&lt;/code&gt; status.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing a Sample Application
&lt;/h2&gt;

&lt;p&gt;When learning Kubernetes, it's best to start with simple applications. This lets you focus on &lt;strong&gt;Kubernetes concepts&lt;/strong&gt; rather than application complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Simple Applications?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fewer moving parts&lt;/strong&gt; — Easier to debug&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick feedback&lt;/strong&gt; — Fast deployment cycles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on fundamentals&lt;/strong&gt; — Learn the platform, not the app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Our Choice: NGINX
&lt;/h3&gt;

&lt;p&gt;We'll deploy &lt;strong&gt;NGINX&lt;/strong&gt; — a lightweight web server that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starts quickly&lt;/li&gt;
&lt;li&gt;Has a small image size&lt;/li&gt;
&lt;li&gt;Returns a simple webpage (easy to verify)&lt;/li&gt;
&lt;li&gt;Is widely used in examples and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Later, we'll also explore deploying with Helm using a different application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Kubernetes Deployments
&lt;/h2&gt;

&lt;p&gt;Before we deploy anything, let's understand what a &lt;strong&gt;Deployment&lt;/strong&gt; is.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Deployment?
&lt;/h3&gt;

&lt;p&gt;A Deployment is a Kubernetes resource that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declares the desired state&lt;/strong&gt; of your application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manages Pods&lt;/strong&gt; (the smallest deployable units)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handles updates&lt;/strong&gt; with rolling deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensures availability&lt;/strong&gt; by maintaining replica counts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as telling Kubernetes: &lt;em&gt;"I want 3 copies of this application running at all times."&lt;/em&gt; Kubernetes then makes it happen and keeps it that way.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Pod?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Pod&lt;/strong&gt; is the smallest unit in Kubernetes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains one or more containers&lt;/li&gt;
&lt;li&gt;Shares networking and storage&lt;/li&gt;
&lt;li&gt;Has a unique IP address within the cluster&lt;/li&gt;
&lt;li&gt;Is ephemeral (can be replaced at any time)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; You rarely create Pods directly. Instead, you create Deployments that manage Pods for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment YAML Structure
&lt;/h3&gt;

&lt;p&gt;Here's what a minimal Deployment looks 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;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;nginx-deployment&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;nginx&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;3&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;nginx&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;nginx&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;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;nginx&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;nginx:1.25&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the YAML
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&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;code&gt;apiVersion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API version for this resource type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kind&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type of resource (Deployment)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;metadata.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the Deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spec.replicas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of Pod copies to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spec.selector&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How the Deployment finds its Pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spec.template&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Template for creating Pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;containers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List of containers in each Pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Docker image to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;containerPort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Port the container listens on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;New to kubernetes? you can read my &lt;a href="https://dev.to/bansikah/kubernetes-for-beginners-69h"&gt;Kubernetes for beginners&lt;/a&gt; post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying Our First Application
&lt;/h2&gt;

&lt;p&gt;Let's deploy NGINX to our EKS cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Deployment
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;nginx-deployment.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;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;nginx-deployment&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;nginx&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;3&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;nginx&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;nginx&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;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;nginx&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;nginx:1.25&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We've added resource requests and limits. This is a best practice that helps Kubernetes schedule Pods efficiently and prevents any single Pod from consuming too many resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Apply the Deployment
&lt;/h3&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; nginx-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment.apps/nginx-deployment created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Verify the Deployment
&lt;/h3&gt;

&lt;p&gt;Check the Deployment status:&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 deployments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           30s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the Pods:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-6d6565499c-2xjkl   1/1     Running   0          45s
nginx-deployment-6d6565499c-8mzft   1/1     Running   0          45s
nginx-deployment-6d6565499c-vwxyz   1/1     Running   0          45s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All 3 replicas are running!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: View More Details
&lt;/h3&gt;

&lt;p&gt;For detailed information about a Pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see which nodes the Pods are running on:&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;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Understanding Kubernetes Services
&lt;/h2&gt;

&lt;p&gt;Our NGINX Pods are running, but how do we access them?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pods have IP addresses, but they're &lt;strong&gt;internal&lt;/strong&gt; to the cluster&lt;/li&gt;
&lt;li&gt;Pods are &lt;strong&gt;ephemeral&lt;/strong&gt; — they can be replaced at any time&lt;/li&gt;
&lt;li&gt;Each Pod has a &lt;strong&gt;different IP&lt;/strong&gt; address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try to access a Pod directly by IP, what happens when that Pod dies and a new one takes its place with a different IP?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Services
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Service&lt;/strong&gt; provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stable endpoint&lt;/strong&gt; — A single IP/DNS name that doesn't change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load balancing&lt;/strong&gt; — Distributes traffic across all matching Pods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service discovery&lt;/strong&gt; — Other apps can find your service by name&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Service Types
&lt;/h3&gt;

&lt;p&gt;Kubernetes offers several Service types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClusterIP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Internal IP only (default)&lt;/td&gt;
&lt;td&gt;Internal communication between services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NodePort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exposes on each node's IP at a static port&lt;/td&gt;
&lt;td&gt;Development, testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LoadBalancer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provisions an external load balancer&lt;/td&gt;
&lt;td&gt;Production traffic from internet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ClusterIP (Default)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only accessible &lt;strong&gt;within&lt;/strong&gt; the cluster&lt;/li&gt;
&lt;li&gt;Great for internal services (databases, caches, etc.)&lt;/li&gt;
&lt;li&gt;Other Pods can access via &lt;code&gt;service-name.namespace.svc.cluster.local&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NodePort
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exposes the service on each node's IP at a specific port (30000-32767)&lt;/li&gt;
&lt;li&gt;Accessible from outside the cluster via &lt;code&gt;&amp;lt;NodeIP&amp;gt;:&amp;lt;NodePort&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Not recommended for production (no load balancing across nodes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  LoadBalancer
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Best for production workloads on AWS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Automatically provisions an AWS Elastic Load Balancer (ELB)&lt;/li&gt;
&lt;li&gt;Provides a public DNS name&lt;/li&gt;
&lt;li&gt;Handles SSL termination (with proper configuration)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Exposing Our Application with LoadBalancer
&lt;/h2&gt;

&lt;p&gt;Let's create a LoadBalancer Service to expose our NGINX deployment to the internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Service
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;nginx-service.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;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;Service&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;nginx-service&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;nginx&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Service YAML
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&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;code&gt;type: LoadBalancer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tells Kubernetes to provision an external load balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;selector.app: nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Routes traffic to Pods with label &lt;code&gt;app: nginx&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;port: 80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Port the Service listens on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;targetPort: 80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Port on the Pods to forward traffic to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 2: Apply the Service
&lt;/h3&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; nginx-service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service/nginx-service created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Watch the Service Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get service nginx-service &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, you'll see &lt;code&gt;&amp;lt;pending&amp;gt;&lt;/code&gt; for the external IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME            TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-service   LoadBalancer   172.20.10.123   &amp;lt;pending&amp;gt;     80:31234/TCP   10s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 1-2 minutes, AWS provisions the load balancer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME            TYPE           CLUSTER-IP      EXTERNAL-IP                                                              PORT(S)        AGE
nginx-service   LoadBalancer   172.20.10.123   a1b2c3d4e5f6g7h8i9.us-east-1.elb.amazonaws.com   80:31234/TCP   90s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;Ctrl+C&lt;/code&gt; to stop watching.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happened Behind the Scenes?
&lt;/h3&gt;

&lt;p&gt;When you created the LoadBalancer Service:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt; detected the Service type and called the AWS cloud controller&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS&lt;/strong&gt; provisioned a Classic Load Balancer (or NLB with annotations)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ELB&lt;/strong&gt; was configured to forward traffic to your worker nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-proxy&lt;/strong&gt; on each node routes traffic to the correct Pods&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This integration is one of the powerful benefits of running Kubernetes on AWS!&lt;/p&gt;




&lt;h2&gt;
  
  
  Verifying the Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Get the Load Balancer URL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get service nginx-service &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.loadBalancer.ingress[0].hostname}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or simply:&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 svc nginx-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access via Browser
&lt;/h3&gt;

&lt;p&gt;Open the EXTERNAL-IP URL in your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://a1b2c3d4e5f6g7h8i9.us-east-1.elb.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the default NGINX welcome page!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u5xsj1p7b7kaejt1672.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u5xsj1p7b7kaejt1672.png" alt="Nginx Deployment" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Access via curl
&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;# Store the LoadBalancer URL in a variable and curl it&lt;/span&gt;
&lt;span class="nv"&gt;LB_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get svc nginx-service &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.loadBalancer.ingress[0].hostname}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl http://&lt;span class="nv"&gt;$LB_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftgt2jlyb7bzukz0f85r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftgt2jlyb7bzukz0f85r.png" alt=" " width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Pod Logs
&lt;/h3&gt;

&lt;p&gt;View the access logs from NGINX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &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;nginx &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see HTTP request logs from your browser/curl requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scaling the Application
&lt;/h2&gt;

&lt;p&gt;One of Kubernetes' strengths is easy scaling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scale Up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployment nginx-deployment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&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 pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have 5 NGINX Pods!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmowuapzcdamup5fh48t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmowuapzcdamup5fh48t.png" alt="Scale pods" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Scale Down
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployment nginx-deployment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Kubernetes will gracefully terminate 3 Pods.&lt;/p&gt;
&lt;h3&gt;
  
  
  Autoscaling (Preview)
&lt;/h3&gt;

&lt;p&gt;For automatic scaling based on CPU/memory, you can use the Horizontal Pod Autoscaler (HPA). We'll cover this in a future article.&lt;/p&gt;


&lt;h2&gt;
  
  
  Introduction to Helm
&lt;/h2&gt;

&lt;p&gt;So far, we've been writing YAML files manually. This works, but what happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to deploy to multiple environments?&lt;/li&gt;
&lt;li&gt;You want to share your deployment with others?&lt;/li&gt;
&lt;li&gt;Applications require dozens of YAML files?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What is Helm?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Helm&lt;/strong&gt; is the package manager for Kubernetes. Think of it like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apt&lt;/code&gt; for Ubuntu&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;brew&lt;/code&gt; for macOS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm&lt;/code&gt; for Node.js&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Key Concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A package containing all Kubernetes manifests for an application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Release&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A deployed instance of a chart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A collection of charts (like a package registry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Values&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configuration options to customize a chart&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Why Use Helm?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Templating&lt;/strong&gt; — Use variables instead of hardcoded values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt; — Package once, deploy many times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning&lt;/strong&gt; — Track and rollback releases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community&lt;/strong&gt; — Thousands of pre-built charts available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt; — Deploy complex apps with one command&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Helm vs Raw YAML
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Raw YAML&lt;/th&gt;
&lt;th&gt;Helm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Slightly higher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;Full control&lt;/td&gt;
&lt;td&gt;Template-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusability&lt;/td&gt;
&lt;td&gt;Copy-paste&lt;/td&gt;
&lt;td&gt;Charts and values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex apps&lt;/td&gt;
&lt;td&gt;Many files to manage&lt;/td&gt;
&lt;td&gt;Single command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment config&lt;/td&gt;
&lt;td&gt;Manual changes&lt;/td&gt;
&lt;td&gt;Values files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Installing Helm
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install Helm CLI
&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;# Linux&lt;/span&gt;
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;helm

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
helm version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Helm Basics
&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;# Add a chart repository (example with prometheus-community)&lt;/span&gt;
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

&lt;span class="c"&gt;# Update repository information&lt;/span&gt;
helm repo update

&lt;span class="c"&gt;# Search for charts&lt;/span&gt;
helm search repo prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Many popular Helm chart repositories like Bitnami have moved to enterprise licensing models. When choosing charts, prefer community-maintained repositories or official project charts. Always check the license and image sources before using a chart in production.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Deploying Apache (Manual Approach)
&lt;/h2&gt;

&lt;p&gt;While Helm is powerful, sometimes the simplest approach is best. Let's deploy Apache using the official Docker Hub image with manual manifests - similar to how we deployed NGINX.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Manual Deployment?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No licensing concerns&lt;/strong&gt; - Uses official Docker Hub images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full control&lt;/strong&gt; - You understand exactly what's deployed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler debugging&lt;/strong&gt; - Fewer abstractions to troubleshoot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning value&lt;/strong&gt; - Reinforces Kubernetes concepts&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 1: Create the Apache Deployment
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;apache-deployment.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;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;apache-deployment&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;apache&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;3&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;apache&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;apache&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;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;apache&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;httpd:2.4&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;httpd:2.4&lt;/code&gt; image is the official Apache HTTP Server image from Docker Hub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the Apache Service
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;apache-service.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;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;Service&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;apache-service&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;apache&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&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;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Deploy Apache
&lt;/h3&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; apache-deployment.yaml &lt;span class="nt"&gt;-f&lt;/span&gt; apache-service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment.apps/apache-deployment created
service/apache-service created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Verify the Deployment
&lt;/h3&gt;

&lt;p&gt;Check the pods:&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;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;apache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                               READY   STATUS    RESTARTS   AGE
apache-deployment-6c57ff8d-cdfzr   1/1     Running   0          75s
apache-deployment-6c57ff8d-nbdjq   1/1     Running   0          75s
apache-deployment-6c57ff8d-xqv5f   1/1     Running   0          75s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Get the LoadBalancer URL
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc apache-service &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the EXTERNAL-IP to be assigned (1-2 minutes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME             TYPE           CLUSTER-IP      EXTERNAL-IP                                                              PORT(S)        AGE
apache-service   LoadBalancer   172.20.86.183   a4108c17eb1b441978cb345f8622d540-727057055.us-east-1.elb.amazonaws.com   80:31783/TCP   60s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Test the Application
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AWS ELB DNS propagation can take 2-5 minutes. If you get a DNS resolution error, wait a moment and try again.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LB_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get svc apache-service &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.loadBalancer.ingress[0].hostname}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl http://&lt;span class="nv"&gt;$LB_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;&lt;/span&gt;It works!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or YOu can check on the browser :&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mkdd4vzou9s9h75qvjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mkdd4vzou9s9h75qvjv.png" alt="Apache browser" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have two applications running on your EKS cluster - NGINX and Apache!&lt;/p&gt;


&lt;h2&gt;
  
  
  When to Use Helm
&lt;/h2&gt;

&lt;p&gt;Helm shines for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex applications&lt;/strong&gt; with many interdependent resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community charts&lt;/strong&gt; from trusted sources (check licenses!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-specific configurations&lt;/strong&gt; using values files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release management&lt;/strong&gt; with rollback capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simple deployments like web servers, manual manifests are often clearer and easier to maintain.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example: Using Helm with Community Charts
&lt;/h3&gt;

&lt;p&gt;Here's how you might use Helm with a community-maintained chart:&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;# Add a repository&lt;/span&gt;
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

&lt;span class="c"&gt;# Install with custom values&lt;/span&gt;
helm &lt;span class="nb"&gt;install &lt;/span&gt;my-prometheus prometheus-community/prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; server.persistentVolume.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# List releases&lt;/span&gt;
helm list

&lt;span class="c"&gt;# Upgrade a release&lt;/span&gt;
helm upgrade my-prometheus prometheus-community/prometheus &lt;span class="nt"&gt;--set&lt;/span&gt; server.replicas&lt;span class="o"&gt;=&lt;/span&gt;2

&lt;span class="c"&gt;# View history&lt;/span&gt;
helm &lt;span class="nb"&gt;history &lt;/span&gt;my-prometheus

&lt;span class="c"&gt;# Rollback if needed&lt;/span&gt;
helm rollback my-prometheus 1

&lt;span class="c"&gt;# Uninstall&lt;/span&gt;
helm uninstall my-prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;For reference, here's the complete file structure for this article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;k8s-manifests/
├── README.md                    # Usage instructions
├── nginx-deployment.yaml        # NGINX Deployment manifest
├── nginx-service.yaml           # NGINX LoadBalancer Service 
├── apache-deployment.yaml       # Apache Deployment manifest
└── apache-service.yaml          # Apache LoadBalancer Service 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All files are available in the series repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  Clean Up
&lt;/h2&gt;

&lt;p&gt;Let's clean up the resources we created:&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;# Navigate to the k8s-manifests directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;k8s-manifests

&lt;span class="c"&gt;# Delete NGINX deployment and service&lt;/span&gt;
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; nginx-service.yaml
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; nginx-deployment.yaml

&lt;span class="c"&gt;# Delete Apache deployment and service&lt;/span&gt;
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; apache-service.yaml
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; apache-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify everything is removed:&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 all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Deployments&lt;/strong&gt; — Declare and manage your application Pods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pods&lt;/strong&gt; — The smallest deployable units in Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services&lt;/strong&gt; — Provide stable endpoints and load balancing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoadBalancer Services&lt;/strong&gt; — Automatically provision AWS ELBs for external access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling&lt;/strong&gt; — Easily scale applications up or down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm&lt;/strong&gt; — Package manager for Kubernetes that simplifies deployments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deployments manage Pods and ensure your desired state is maintained&lt;/li&gt;
&lt;li&gt;Services provide stable networking for ephemeral Pods&lt;/li&gt;
&lt;li&gt;LoadBalancer Services integrate with AWS to provision ELBs automatically&lt;/li&gt;
&lt;li&gt;Helm simplifies deploying complex applications with a single command&lt;/li&gt;
&lt;li&gt;Always use resource requests and limits for production workloads&lt;/li&gt;
&lt;li&gt;Labels are crucial for connecting Deployments and Services&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Any doubts in the code you can always refer to the github repo &lt;a href="https://github.com/bansikah22/eks-at-scale/tree/master/k8s-manifests" rel="noopener noreferrer"&gt;Manifest files&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;In &lt;strong&gt;Part 5&lt;/strong&gt;, we'll introduce &lt;strong&gt;GitOps&lt;/strong&gt; and manage our deployments using &lt;strong&gt;Argo CD&lt;/strong&gt;. You'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What GitOps is and why it matters&lt;/li&gt;
&lt;li&gt;Setting up Argo CD on EKS&lt;/li&gt;
&lt;li&gt;Deploying applications from Git repositories&lt;/li&gt;
&lt;li&gt;Automated sync and self-healing deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" rel="noopener noreferrer"&gt;Kubernetes Deployments Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener noreferrer"&gt;Kubernetes Services Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://helm.sh/docs/" rel="noopener noreferrer"&gt;Helm Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/load-balancing.html" rel="noopener noreferrer"&gt;Amazon EKS Load Balancing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this article helpful? Follow along with the series and drop your questions in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>eks</category>
      <category>devops</category>
    </item>
    <item>
      <title>Amazon EKS Series - Part 3: Provisioning an EKS Cluster with Terraform</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Wed, 17 Dec 2025 14:33:53 +0000</pubDate>
      <link>https://dev.to/bansikah/amazon-eks-series-part-3-provisioning-an-eks-cluster-with-terraform-413c</link>
      <guid>https://dev.to/bansikah/amazon-eks-series-part-3-provisioning-an-eks-cluster-with-terraform-413c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome back to the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/bansikah/amazon-eks-series-part-2-eks-architecture-and-core-components-36gk"&gt;Part 2&lt;/a&gt;, we explored the architecture of Amazon EKS — the control plane, worker nodes, networking, and IAM integration. Now it's time to put that knowledge into practice.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a Terraform project for EKS&lt;/li&gt;
&lt;li&gt;Use community modules for VPC and EKS&lt;/li&gt;
&lt;li&gt;Provision a production-ready EKS cluster&lt;/li&gt;
&lt;li&gt;Verify your cluster with kubectl&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What we're building:&lt;/strong&gt; A fully functional EKS cluster with a VPC, private subnets, and managed node groups — all defined as code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Terraform for EKS?
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;eksctl&lt;/code&gt; is great for quick setups (as we saw in Part 1), production environments demand more control and repeatability. That's where &lt;strong&gt;Terraform&lt;/strong&gt; shines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of Infrastructure as Code (IaC)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility&lt;/strong&gt; — Create identical clusters across dev, staging, and production environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt; — Track all infrastructure changes in Git with full history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Collaboration&lt;/strong&gt; — Review infrastructure changes through pull requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt; — Integrate with CI/CD pipelines for automated deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; — Your code serves as living documentation of your infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Community Modules?
&lt;/h3&gt;

&lt;p&gt;We'll use the official Terraform AWS modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform-aws-modules/vpc/aws&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform-aws-modules/eks/aws&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These modules are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Production-tested&lt;/strong&gt; — Used by thousands of organizations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actively maintained&lt;/strong&gt; — Regular updates and security patches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-documented&lt;/strong&gt; — Comprehensive examples and documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interview-friendly&lt;/strong&gt; — Widely recognized in the industry&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tools &amp;amp; Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, ensure you have the following installed and configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Version&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;AWS CLI&lt;/td&gt;
&lt;td&gt;v2.x&lt;/td&gt;
&lt;td&gt;AWS authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.0&lt;/td&gt;
&lt;td&gt;Infrastructure provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kubectl&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;Kubernetes cluster interaction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Installing the Tools
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AWS CLI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;awscli

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
aws &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Terraform
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux (Ubuntu/Debian)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gnupg software-properties-common
wget &lt;span class="nt"&gt;-O-&lt;/span&gt; https://apt.releases.hashicorp.com/gpg | gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /usr/share/keyrings/hashicorp-archive-keyring.gpg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/hashicorp.list
&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;terraform

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew tap hashicorp/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;hashicorp/tap/terraform

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
terraform &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  kubectl
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="s2"&gt;"https://dl.k8s.io/release/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; https://dl.k8s.io/release/stable.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/bin/linux/amd64/kubectl"&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x kubectl
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;kubectl /usr/local/bin/

&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;kubectl

&lt;span class="c"&gt;# Verify installation&lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS Credentials
&lt;/h3&gt;

&lt;p&gt;Configure your AWS credentials using one of these methods:&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: AWS Configure (Recommended for beginners)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll be prompted for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Access Key ID&lt;/li&gt;
&lt;li&gt;AWS Secret Access Key&lt;/li&gt;
&lt;li&gt;Default region (e.g., &lt;code&gt;us-east-1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Default output format (e.g., &lt;code&gt;json&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Option 2: Environment Variables
&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;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-access-key"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-secret-key"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Verify AWS Configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify AWS CLI is configured&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your AWS account ID and user/role information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AIDAXXXXXXXXXXXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:user/your-username"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IAM Permissions Required
&lt;/h3&gt;

&lt;p&gt;Your AWS user/role needs permissions to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC and networking resources (subnets, NAT gateway, etc.)&lt;/li&gt;
&lt;li&gt;EKS clusters and node groups&lt;/li&gt;
&lt;li&gt;IAM roles and policies&lt;/li&gt;
&lt;li&gt;EC2 instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For learning purposes, you can use the &lt;code&gt;AdministratorAccess&lt;/code&gt; policy. For production, follow the principle of least privilege.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;A clean project structure makes your Terraform code maintainable. Here's what we'll create or you can visit this repository to see the code &lt;a href="https://github.com/bansikah22/eks-at-scale" rel="noopener noreferrer"&gt;EKS terraform code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eks-terraform/
├── main.tf          # Main resources (VPC &amp;amp; EKS modules)
├── providers.tf     # Provider configuration
├── versions.tf      # Terraform and provider versions
├── variables.tf     # Input variables
└── outputs.tf       # Output values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Structure?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;versions.tf&lt;/code&gt;&lt;/strong&gt; — Pin Terraform and provider versions for consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;providers.tf&lt;/code&gt;&lt;/strong&gt; — Configure AWS provider settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;/strong&gt; — Define configurable inputs (region, cluster name, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/strong&gt; — The core infrastructure definitions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;outputs.tf&lt;/code&gt;&lt;/strong&gt; — Expose useful values after deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation keeps your code organized and easy to navigate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Terraform Versions
&lt;/h2&gt;

&lt;p&gt;First, let's pin our Terraform and provider versions. This ensures consistent behavior across different machines and CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;versions.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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&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;"~&amp;gt; 5.0"&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;Why version pinning matters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents unexpected breaking changes&lt;/li&gt;
&lt;li&gt;Ensures team members use compatible versions&lt;/li&gt;
&lt;li&gt;Makes builds reproducible over time&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Provider Configuration
&lt;/h2&gt;

&lt;p&gt;Configure the AWS provider with the region variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;providers.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;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&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;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple and clean — the region comes from our variables file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Input Variables
&lt;/h2&gt;

&lt;p&gt;Define the configurable parameters for your infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;variables.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;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&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;"AWS region to deploy resources"&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;"us-east-1"&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;"cluster_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 EKS cluster"&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;"eks-cluster"&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;"cluster_version"&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;"Kubernetes version for the EKS cluster"&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;"1.31"&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;"vpc_cidr"&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;"CIDR block for VPC"&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;"10.0.0.0/16"&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;"environment"&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;"Environment name (e.g., dev, staging, prod)"&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;"dev"&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;Key variables explained:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;aws_region&lt;/code&gt;&lt;/strong&gt; — Where your cluster will be deployed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cluster_name&lt;/code&gt;&lt;/strong&gt; — Identifier for your EKS cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cluster_version&lt;/code&gt;&lt;/strong&gt; — Kubernetes version (use a recent stable version)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;vpc_cidr&lt;/code&gt;&lt;/strong&gt; — IP address range for your VPC (10.0.0.0/16 gives you 65,536 IPs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/strong&gt; — Useful for tagging and distinguishing environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Main Configuration (VPC &amp;amp; EKS)
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. We'll use community modules to create our VPC and EKS cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;main.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="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# Data Sources&lt;/span&gt;
&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filter&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;"opt-in-status"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"opt-in-not-required"&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="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# Local Variables&lt;/span&gt;
&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&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_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&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="mi"&gt;3&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&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;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Terraform&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eks-at-scale"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# VPC Module&lt;/span&gt;
&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&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;"~&amp;gt; 5.0"&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;"${var.cluster_name}-vpc"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&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;vpc_cidr&lt;/span&gt;

  &lt;span class="nx"&gt;azs&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;azs&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&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;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&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;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&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;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&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;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&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;single_nat_gateway&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;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Tags required for EKS&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/elb"&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="nx"&gt;private_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/internal-elb"&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="nx"&gt;tags&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;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# EKS Module&lt;/span&gt;
&lt;span class="c1"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/eks/aws"&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;"~&amp;gt; 20.0"&lt;/span&gt;

  &lt;span class="nx"&gt;cluster_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;

  &lt;span class="nx"&gt;cluster_endpoint_public_access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Cluster Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_addons&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;coredns&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;eks-pod-identity-agent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;kube-proxy&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;vpc-cni&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;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;

  &lt;span class="c1"&gt;# EKS Managed Node Group(s)&lt;/span&gt;
  &lt;span class="nx"&gt;eks_managed_node_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;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ami_type&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AL2023_x86_64_STANDARD"&lt;/span&gt;
      &lt;span class="nx"&gt;instance_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t3.medium"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;min_size&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;max_size&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
      &lt;span class="nx"&gt;desired_size&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="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Cluster access entry&lt;/span&gt;
  &lt;span class="nx"&gt;enable_cluster_creator_admin_permissions&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;tags&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;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the Configuration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Data Sources
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&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;Fetches available AZs in the region, filtering out any that require opt-in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Local Variables
&lt;/h4&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;azs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&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_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&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="mi"&gt;3&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;ul&gt;
&lt;li&gt;Selects the first 3 availability zones&lt;/li&gt;
&lt;li&gt;Defines common tags for all resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  VPC Module
&lt;/h4&gt;

&lt;p&gt;The VPC module creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC&lt;/strong&gt; with the specified CIDR block&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public subnets&lt;/strong&gt; — For load balancers and NAT gateways&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private subnets&lt;/strong&gt; — For worker nodes (more secure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT Gateway&lt;/strong&gt; — Allows private subnets to access the internet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Required tags&lt;/strong&gt; — EKS uses these tags to discover subnets for load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Subnet tagging is critical:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubernetes.io/role/elb = 1&lt;/code&gt; — Public subnets for internet-facing load balancers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubernetes.io/role/internal-elb = 1&lt;/code&gt; — Private subnets for internal load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  EKS Module
&lt;/h4&gt;

&lt;p&gt;The EKS module creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EKS Cluster&lt;/strong&gt; — The managed control plane&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Add-ons&lt;/strong&gt; — Essential components (CoreDNS, VPC CNI, kube-proxy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Node Group&lt;/strong&gt; — Worker nodes with auto-scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Node group configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ami_type: AL2023_x86_64_STANDARD&lt;/code&gt; — Amazon Linux 2023 (latest EKS-optimized AMI)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;instance_types: ["t3.medium"]&lt;/code&gt; — 2 vCPUs, 4GB RAM (good for learning)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;min_size: 2, max_size: 4&lt;/code&gt; — Auto-scaling boundaries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;desired_size: 2&lt;/code&gt; — Initial number of nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on Instance Types:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For learning and testing purposes, it is possible to use small instance types such as &lt;code&gt;t2.micro&lt;/code&gt;. However, due to limited CPU and memory, this setup is not recommended for running real workloads. For a more stable experience, instance types like &lt;code&gt;t3.small&lt;/code&gt; or &lt;code&gt;t3.medium&lt;/code&gt; are preferred.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Outputs
&lt;/h2&gt;

&lt;p&gt;Define useful values to display after deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;outputs.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;output&lt;/span&gt; &lt;span class="s2"&gt;"cluster_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 EKS cluster"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"cluster_endpoint"&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;"Endpoint for EKS control plane"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_endpoint&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"cluster_version"&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;"Kubernetes version of the cluster"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"cluster_security_group_id"&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;"Security group ID attached to the EKS cluster"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_security_group_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"region"&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;"AWS region"&lt;/span&gt;
  &lt;span class="nx"&gt;value&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;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_id"&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;"VPC ID"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"configure_kubectl"&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;"Command to configure kubectl"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks update-kubeconfig --region ${var.aws_region} --name ${module.eks.cluster_name}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;configure_kubectl&lt;/code&gt; output is especially helpful — it gives you the exact command to run after deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Deploy the Infrastructure
&lt;/h2&gt;

&lt;p&gt;Now let's provision our EKS cluster!&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize Terraform
&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;cd &lt;/span&gt;eks-terraform
terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads the required providers and modules. You'll see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing provider plugins...
- Finding hashicorp/aws versions matching "~&amp;gt; 5.0"...
- Installing hashicorp/aws v5.x.x...

Terraform has been successfully initialized!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review the Plan
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform validate

terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows what Terraform will create without making any changes. Review the output carefully — you should see resources for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC and subnets&lt;/li&gt;
&lt;li&gt;Internet Gateway and NAT Gateway&lt;/li&gt;
&lt;li&gt;Route tables&lt;/li&gt;
&lt;li&gt;EKS cluster&lt;/li&gt;
&lt;li&gt;Managed node group&lt;/li&gt;
&lt;li&gt;Security groups&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Apply the Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted to confirm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This process takes approximately 10-15 minutes. EKS cluster creation is the longest step.&lt;/p&gt;

&lt;p&gt;You'll see progress output as resources are created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.vpc.aws_vpc.this[0]: Creating...
module.vpc.aws_vpc.this[0]: Creation complete after 2s
...
module.eks.aws_eks_cluster.this[0]: Creating...
module.eks.aws_eks_cluster.this[0]: Still creating... [5m0s elapsed]
module.eks.aws_eks_cluster.this[0]: Creation complete after 9m32s
...
Apply complete! Resources: 65 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also login to your aws account and you will see your newly created cluster &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftr1n7irfh1tguz92ehq1.png" alt="Cluster on aws" width="800" height="382"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 7: Configure kubectl &amp;amp; Verify
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Update kubeconfig
&lt;/h3&gt;

&lt;p&gt;After successful deployment, configure kubectl to connect to your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws eks update-kubeconfig &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="nt"&gt;--name&lt;/span&gt; eks-cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the output from Terraform:&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="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; configure_kubectl&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Added new context arn:aws:eks:us-east-1:ACCOUNT_ID:cluster/eks-cluster to /home/user/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the Cluster
&lt;/h3&gt;

&lt;p&gt;Check that your nodes are ready:&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 nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                             STATUS   ROLES    AGE   VERSION
ip-10-0-1-xxx.ec2.internal      Ready    &amp;lt;none&amp;gt;   5m    v1.31.x
ip-10-0-2-xxx.ec2.internal      Ready    &amp;lt;none&amp;gt;   5m    v1.31.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check cluster information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl cluster-info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify system pods are 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; kube-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see CoreDNS, kube-proxy, and VPC CNI pods running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7qh9it7madjse19i53d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7qh9it7madjse19i53d.png" alt="get pods" width="800" height="146"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Clean Up (Important!)
&lt;/h2&gt;

&lt;p&gt;To avoid unnecessary AWS charges, destroy the resources when you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; to confirm. This will remove all resources created by Terraform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This is irreversible and will delete your entire cluster and VPC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35biexfxjxkvpcasrfeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35biexfxjxkvpcasrfeq.png" alt="TF destroy" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for IaC
&lt;/h2&gt;

&lt;p&gt;Here are key best practices to follow as you work with Terraform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use community modules&lt;/strong&gt; — Don't reinvent the wheel; leverage tested modules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin versions&lt;/strong&gt; — Lock Terraform, provider, and module versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep state remote&lt;/strong&gt; — Use S3 + DynamoDB for team collaboration (we'll cover this later)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate environments&lt;/strong&gt; — Use workspaces or separate directories for dev/staging/prod&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use variables&lt;/strong&gt; — Make your code reusable across environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag everything&lt;/strong&gt; — Tags help with cost tracking and resource management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review plans carefully&lt;/strong&gt; — Always check &lt;code&gt;terraform plan&lt;/code&gt; before applying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use .gitignore&lt;/strong&gt; — Never commit &lt;code&gt;.terraform/&lt;/code&gt; or &lt;code&gt;*.tfstate&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a clean Terraform project structure&lt;/li&gt;
&lt;li&gt;Used community modules for VPC and EKS&lt;/li&gt;
&lt;li&gt;Provisioned a production-ready EKS cluster&lt;/li&gt;
&lt;li&gt;Configured kubectl and verified the cluster&lt;/li&gt;
&lt;li&gt;Learned IaC best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have a repeatable, version-controlled way to create EKS clusters!&lt;/p&gt;




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

&lt;p&gt;In &lt;strong&gt;Part 4&lt;/strong&gt;, we'll deploy applications to our EKS cluster and explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying a sample application&lt;/li&gt;
&lt;li&gt;Understanding Kubernetes Deployments and Services&lt;/li&gt;
&lt;li&gt;Exposing applications with Load Balancers&lt;/li&gt;
&lt;li&gt;Introduction to Helm for package management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest" rel="noopener noreferrer"&gt;Terraform AWS VPC Module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest" rel="noopener noreferrer"&gt;Terraform AWS EKS Module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.github.io/aws-eks-best-practices/" rel="noopener noreferrer"&gt;Amazon EKS Best Practices Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs" rel="noopener noreferrer"&gt;Terraform Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Code Repository
&lt;/h2&gt;

&lt;p&gt;All code from this article is available in the &lt;a href="https://github.com/bansikah22/eks-at-scale/tree/master/eks-terraform" rel="noopener noreferrer"&gt;eks-terraform/&lt;/a&gt; directory of the series repository.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this article helpful? Follow along with the series and drop a comment with your questions!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>terraform</category>
      <category>devops</category>
    </item>
    <item>
      <title>Amazon EKS Series - Part 2: EKS Architecture and Core Components</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Wed, 17 Dec 2025 13:40:55 +0000</pubDate>
      <link>https://dev.to/bansikah/amazon-eks-series-part-2-eks-architecture-and-core-components-36gk</link>
      <guid>https://dev.to/bansikah/amazon-eks-series-part-2-eks-architecture-and-core-components-36gk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome back to the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/bansikah/amazon-eks-series-part-1-introduction-to-eks-4m3p"&gt;Part 1&lt;/a&gt;, we covered the fundamentals of Amazon EKS — what it is, why to use it, and the different ways to manage worker nodes. We also created our first EKS cluster using &lt;code&gt;eksctl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll take a deeper look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The high-level architecture of Amazon EKS&lt;/li&gt;
&lt;li&gt;Control plane components (AWS-managed)&lt;/li&gt;
&lt;li&gt;Worker nodes (customer-managed)&lt;/li&gt;
&lt;li&gt;Networking fundamentals&lt;/li&gt;
&lt;li&gt;IAM and authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding this architecture is essential before diving into production deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  High-Level View of Amazon EKS
&lt;/h2&gt;

&lt;p&gt;Amazon EKS is a &lt;strong&gt;managed Kubernetes service&lt;/strong&gt; that runs the Kubernetes control plane for you. This means AWS handles the complex, undifferentiated heavy lifting of running Kubernetes, while you focus on deploying and managing your applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c6y6pufasv90gsjj3uj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c6y6pufasv90gsjj3uj.png" alt="EKS Architecture" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What AWS Manages vs What You Manage
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Managed By&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Control Plane (API Server, etcd, Scheduler, Controllers)&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Plane High Availability&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Plane Security Patches&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker Nodes&lt;/td&gt;
&lt;td&gt;You (or AWS with Managed Node Groups/Fargate)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Deployments&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pod Networking&lt;/td&gt;
&lt;td&gt;You (with AWS VPC CNI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM Roles and Policies&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This shared responsibility model allows you to leverage AWS's expertise in running highly available infrastructure while maintaining control over your workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  EKS Control Plane (AWS-Managed)
&lt;/h2&gt;

&lt;p&gt;The control plane is the brain of your Kubernetes cluster. In EKS, AWS fully manages this component, running it across &lt;strong&gt;multiple Availability Zones&lt;/strong&gt; for high availability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Control Plane Components
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Kubernetes API Server (&lt;code&gt;kube-apiserver&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;The API server is the &lt;strong&gt;front door&lt;/strong&gt; to your Kubernetes cluster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exposes the Kubernetes API over HTTPS&lt;/li&gt;
&lt;li&gt;Validates and processes all API requests (from &lt;code&gt;kubectl&lt;/code&gt;, controllers, and other components)&lt;/li&gt;
&lt;li&gt;Acts as the gateway for all cluster operations — creating pods, services, deployments, etc.&lt;/li&gt;
&lt;li&gt;Authenticates requests using AWS IAM (via the AWS IAM Authenticator)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you run &lt;code&gt;kubectl get pods&lt;/code&gt;, your request goes to the API server, which retrieves the information from etcd and returns it to you.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. etcd
&lt;/h4&gt;

&lt;p&gt;etcd is a &lt;strong&gt;distributed key-value store&lt;/strong&gt; that serves as Kubernetes' database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores all cluster state and configuration data&lt;/li&gt;
&lt;li&gt;Holds information about pods, services, secrets, ConfigMaps, and more&lt;/li&gt;
&lt;li&gt;Provides strong consistency guarantees&lt;/li&gt;
&lt;li&gt;In EKS, AWS manages etcd replication across multiple Availability Zones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You never interact with etcd directly — all access goes through the API server.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Scheduler (&lt;code&gt;kube-scheduler&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;The scheduler is responsible for &lt;strong&gt;placing pods on nodes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watches for newly created pods that have no node assigned&lt;/li&gt;
&lt;li&gt;Evaluates resource requirements (CPU, memory, storage)&lt;/li&gt;
&lt;li&gt;Considers constraints like node selectors, taints, tolerations, and affinity rules&lt;/li&gt;
&lt;li&gt;Selects the most suitable node and binds the pod to it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scheduler ensures efficient resource utilization across your cluster.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Controller Manager (&lt;code&gt;kube-controller-manager&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;The controller manager runs &lt;strong&gt;control loops&lt;/strong&gt; that regulate cluster state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node Controller&lt;/strong&gt; — Monitors node health and responds when nodes go down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replication Controller&lt;/strong&gt; — Ensures the correct number of pod replicas are running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints Controller&lt;/strong&gt; — Populates endpoint objects (joins Services and Pods)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Account Controller&lt;/strong&gt; — Creates default service accounts for new namespaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each controller watches the current state and works to move it toward the desired state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Characteristics of EKS Control Plane
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runs in an AWS-managed VPC&lt;/strong&gt; — Isolated from your account and other customers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly available&lt;/strong&gt; — At least two API server instances and three etcd nodes across multiple AZs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically scaled&lt;/strong&gt; — AWS scales control plane resources based on cluster size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No direct access&lt;/strong&gt; — You cannot SSH into control plane nodes; you interact only via the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic updates&lt;/strong&gt; — AWS handles patching and security updates&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Worker Nodes (Customer-Managed)
&lt;/h2&gt;

&lt;p&gt;Worker nodes are the &lt;strong&gt;compute resources&lt;/strong&gt; where your applications actually run. Unlike the control plane, you are responsible for provisioning and managing worker nodes (unless using Fargate).&lt;/p&gt;

&lt;h3&gt;
  
  
  Worker Node Components
&lt;/h3&gt;

&lt;p&gt;Each worker node runs several Kubernetes components:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. kubelet
&lt;/h4&gt;

&lt;p&gt;The kubelet is the &lt;strong&gt;primary node agent&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registers the node with the Kubernetes API server&lt;/li&gt;
&lt;li&gt;Watches for pods scheduled to its node&lt;/li&gt;
&lt;li&gt;Ensures containers are running and healthy&lt;/li&gt;
&lt;li&gt;Reports node and pod status back to the control plane&lt;/li&gt;
&lt;li&gt;Executes liveness and readiness probes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The kubelet communicates with the container runtime to manage container lifecycle.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. kube-proxy
&lt;/h4&gt;

&lt;p&gt;kube-proxy handles &lt;strong&gt;networking on each node&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintains network rules for pod-to-pod communication&lt;/li&gt;
&lt;li&gt;Implements Kubernetes Services (ClusterIP, NodePort, LoadBalancer)&lt;/li&gt;
&lt;li&gt;Uses iptables or IPVS to route traffic to the correct pods&lt;/li&gt;
&lt;li&gt;Enables service discovery within the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Container Runtime
&lt;/h4&gt;

&lt;p&gt;The container runtime &lt;strong&gt;executes containers&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EKS uses &lt;code&gt;containerd&lt;/code&gt; as the default runtime (Docker support was deprecated in Kubernetes 1.24)&lt;/li&gt;
&lt;li&gt;Pulls container images from registries (like Amazon ECR)&lt;/li&gt;
&lt;li&gt;Creates and manages container processes&lt;/li&gt;
&lt;li&gt;Handles container isolation using Linux namespaces and cgroups&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Worker Node Options in EKS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Node Management&lt;/th&gt;
&lt;th&gt;Scaling&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Self-Managed Nodes&lt;/td&gt;
&lt;td&gt;You manage everything&lt;/td&gt;
&lt;td&gt;Manual or custom&lt;/td&gt;
&lt;td&gt;Full control, custom AMIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed Node Groups&lt;/td&gt;
&lt;td&gt;AWS manages provisioning and updates&lt;/td&gt;
&lt;td&gt;Auto Scaling Groups&lt;/td&gt;
&lt;td&gt;Most production workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Fargate&lt;/td&gt;
&lt;td&gt;AWS manages everything&lt;/td&gt;
&lt;td&gt;Automatic per-pod&lt;/td&gt;
&lt;td&gt;Serverless, variable workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Networking in EKS
&lt;/h2&gt;

&lt;p&gt;Networking is a critical aspect of any Kubernetes deployment. EKS integrates deeply with AWS networking services.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC (Virtual Private Cloud)
&lt;/h3&gt;

&lt;p&gt;Your EKS cluster runs inside a &lt;strong&gt;VPC&lt;/strong&gt; — an isolated virtual network in AWS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides network isolation and security&lt;/li&gt;
&lt;li&gt;You define the IP address range (CIDR block)&lt;/li&gt;
&lt;li&gt;Contains subnets, route tables, and internet gateways&lt;/li&gt;
&lt;li&gt;EKS requires a VPC with subnets in at least two Availability Zones&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Subnets
&lt;/h3&gt;

&lt;p&gt;Subnets divide your VPC into smaller network segments:&lt;/p&gt;

&lt;h4&gt;
  
  
  Public Subnets
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Have a route to an Internet Gateway&lt;/li&gt;
&lt;li&gt;Resources can have public IP addresses&lt;/li&gt;
&lt;li&gt;Used for load balancers and bastion hosts&lt;/li&gt;
&lt;li&gt;NAT Gateways are placed here for private subnet internet access&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Private Subnets
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;No direct route to the internet&lt;/li&gt;
&lt;li&gt;Access the internet via NAT Gateway (for pulling images, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommended for worker nodes&lt;/strong&gt; — provides better security&lt;/li&gt;
&lt;li&gt;Pods and nodes are not directly accessible from the internet&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS VPC CNI (Container Network Interface)
&lt;/h3&gt;

&lt;p&gt;EKS uses the &lt;strong&gt;Amazon VPC CNI plugin&lt;/strong&gt; for pod networking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each pod gets a &lt;strong&gt;real IP address&lt;/strong&gt; from your VPC CIDR range&lt;/li&gt;
&lt;li&gt;Pods can communicate directly with other AWS services (RDS, ElastiCache, etc.)&lt;/li&gt;
&lt;li&gt;No overlay network — native VPC networking performance&lt;/li&gt;
&lt;li&gt;Supports security groups for pods (with specific configurations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The CNI plugin attaches Elastic Network Interfaces (ENIs) to worker nodes&lt;/li&gt;
&lt;li&gt;Each ENI can have multiple secondary IP addresses&lt;/li&gt;
&lt;li&gt;These IPs are assigned to pods running on the node&lt;/li&gt;
&lt;li&gt;Pod-to-pod traffic uses native VPC routing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important consideration:&lt;/strong&gt; The number of pods per node is limited by the number of ENIs and IPs the instance type supports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplified Network Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                         AWS Cloud                           │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                      Your VPC                          │  │
│  │  ┌─────────────────┐    ┌─────────────────┐           │  │
│  │  │  Public Subnet  │    │  Public Subnet  │           │  │
│  │  │   (AZ-1)        │    │   (AZ-2)        │           │  │
│  │  │  Load Balancer  │    │  NAT Gateway    │           │  │
│  │  └─────────────────┘    └─────────────────┘           │  │
│  │  ┌─────────────────┐    ┌─────────────────┐           │  │
│  │  │ Private Subnet  │    │ Private Subnet  │           │  │
│  │  │   (AZ-1)        │    │   (AZ-2)        │           │  │
│  │  │  Worker Nodes   │    │  Worker Nodes   │           │  │
│  │  │  (Pods)         │    │  (Pods)         │           │  │
│  │  └─────────────────┘    └─────────────────┘           │  │
│  └───────────────────────────────────────────────────────┘  │
│                              │                              │
│  ┌───────────────────────────┴───────────────────────────┐  │
│  │           EKS Control Plane (AWS-Managed)             │  │
│  │     API Server  │  etcd  │  Scheduler  │  Controllers │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  IAM and Authentication
&lt;/h2&gt;

&lt;p&gt;EKS uses &lt;strong&gt;AWS IAM&lt;/strong&gt; for authentication, providing a secure and familiar way to manage access to your cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Authentication Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User runs kubectl command&lt;/strong&gt; — e.g., &lt;code&gt;kubectl get pods&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS IAM Authenticator&lt;/strong&gt; — kubectl uses the AWS CLI to get a token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token sent to API Server&lt;/strong&gt; — The token is included in the request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EKS validates the token&lt;/strong&gt; — Confirms the IAM identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes RBAC&lt;/strong&gt; — Determines what the user can do&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  IAM vs Kubernetes RBAC
&lt;/h3&gt;

&lt;p&gt;These are two separate but complementary systems:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;AWS IAM&lt;/th&gt;
&lt;th&gt;Kubernetes RBAC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Who&lt;/em&gt; can access the cluster&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;What&lt;/em&gt; they can do inside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS account level&lt;/td&gt;
&lt;td&gt;Kubernetes cluster level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managed by&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS IAM policies&lt;/td&gt;
&lt;td&gt;Kubernetes Role/ClusterRole&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"User X can call EKS APIs"&lt;/td&gt;
&lt;td&gt;"User X can list pods in namespace Y"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The aws-auth ConfigMap
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;aws-auth&lt;/code&gt; ConfigMap maps IAM identities to Kubernetes users and groups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-auth&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;kube-system&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mapRoles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;- rolearn: arn:aws:iam::123456789012:role/NodeInstanceRole&lt;/span&gt;
      &lt;span class="s"&gt;username: system:node:{{EC2PrivateDNSName}}&lt;/span&gt;
      &lt;span class="s"&gt;groups:&lt;/span&gt;
        &lt;span class="s"&gt;- system:bootstrappers&lt;/span&gt;
        &lt;span class="s"&gt;- system:nodes&lt;/span&gt;
  &lt;span class="na"&gt;mapUsers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;- userarn: arn:aws:iam::123456789012:user/admin&lt;/span&gt;
      &lt;span class="s"&gt;username: admin&lt;/span&gt;
      &lt;span class="s"&gt;groups:&lt;/span&gt;
        &lt;span class="s"&gt;- system:masters&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;Worker nodes use IAM roles mapped in &lt;code&gt;aws-auth&lt;/code&gt; to join the cluster&lt;/li&gt;
&lt;li&gt;You can map IAM users and roles to Kubernetes groups&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;system:masters&lt;/code&gt; group has full cluster admin access&lt;/li&gt;
&lt;li&gt;EKS also supports EKS access entries (a newer, simpler alternative to aws-auth)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices for IAM in EKS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use IAM roles, not users&lt;/strong&gt; — Roles are more secure and support temporary credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow least privilege&lt;/strong&gt; — Grant only the permissions needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use IRSA (IAM Roles for Service Accounts)&lt;/strong&gt; — Allow pods to assume IAM roles for AWS API access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit access regularly&lt;/strong&gt; — Review who has access to your cluster&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we explored the architecture of Amazon EKS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EKS is a managed Kubernetes service&lt;/strong&gt; — AWS handles the control plane, you manage worker nodes and applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control plane components&lt;/strong&gt; — API Server, etcd, Scheduler, and Controller Manager work together to manage cluster state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worker nodes&lt;/strong&gt; — Run kubelet, kube-proxy, and container runtime to execute your workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking&lt;/strong&gt; — EKS uses VPC networking with the AWS VPC CNI plugin, giving pods real VPC IP addresses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM integration&lt;/strong&gt; — Authentication uses AWS IAM, while authorization uses Kubernetes RBAC&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The EKS control plane runs in an AWS-managed VPC, highly available across multiple AZs&lt;/li&gt;
&lt;li&gt;You never access control plane nodes directly — only through the Kubernetes API&lt;/li&gt;
&lt;li&gt;Worker nodes run in your VPC and are your responsibility (unless using Fargate)&lt;/li&gt;
&lt;li&gt;Pods get real VPC IP addresses, enabling direct communication with AWS services&lt;/li&gt;
&lt;li&gt;IAM handles &lt;em&gt;authentication&lt;/em&gt; (who you are), RBAC handles &lt;em&gt;authorization&lt;/em&gt; (what you can do)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;aws-auth&lt;/code&gt; ConfigMap bridges IAM identities to Kubernetes users&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;In &lt;strong&gt;Part 3&lt;/strong&gt;, we'll get hands-on and provision an Amazon EKS cluster using &lt;strong&gt;Terraform&lt;/strong&gt; and community modules. You'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up Terraform for EKS&lt;/li&gt;
&lt;li&gt;Using the terraform-aws-eks module&lt;/li&gt;
&lt;li&gt;Configuring VPC, subnets, and node groups&lt;/li&gt;
&lt;li&gt;Best practices for infrastructure as code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;Amazon EKS Architecture - AWS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/pod-networking.html" rel="noopener noreferrer"&gt;Amazon VPC CNI Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html" rel="noopener noreferrer"&gt;Cluster Authentication - AWS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/overview/components/" rel="noopener noreferrer"&gt;Kubernetes Components&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this article helpful? Follow along with the series and share your thoughts in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>eks</category>
      <category>devops</category>
    </item>
    <item>
      <title>Amazon EKS Series - Part 1: Introduction to EKS</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Wed, 17 Dec 2025 10:40:15 +0000</pubDate>
      <link>https://dev.to/bansikah/amazon-eks-series-part-1-introduction-to-eks-4m3p</link>
      <guid>https://dev.to/bansikah/amazon-eks-series-part-1-introduction-to-eks-4m3p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome to the first part of the &lt;strong&gt;Amazon EKS at Scale&lt;/strong&gt; series! In this series, we'll explore Amazon Elastic Kubernetes Service (EKS) from the ground up, covering everything from basic concepts to advanced cluster management.&lt;/p&gt;

&lt;p&gt;In this article, we'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is Amazon EKS?&lt;/li&gt;
&lt;li&gt;Why use EKS?&lt;/li&gt;
&lt;li&gt;Understanding Worker Node options&lt;/li&gt;
&lt;li&gt;Creating and connecting to an EKS cluster&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Amazon EKS?
&lt;/h2&gt;

&lt;p&gt;Amazon Elastic Kubernetes Service (EKS) is a &lt;strong&gt;managed Kubernetes service&lt;/strong&gt; provided by AWS. It simplifies running Kubernetes by handling the complex parts of cluster management for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  What AWS Manages for You
&lt;/h3&gt;

&lt;p&gt;With EKS, AWS takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provisioning and maintaining master nodes&lt;/strong&gt; — AWS automatically sets up and maintains the master nodes that run the Kubernetes control plane, eliminating the need for you to manage this critical infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Installing control plane processes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Server&lt;/strong&gt; — The front-end for the Kubernetes control plane. It handles all REST requests for modifications to pods, services, and other resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduler&lt;/strong&gt; — Watches for newly created pods with no assigned node and selects a node for them to run on based on resource requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;etcd&lt;/strong&gt; — A consistent and highly-available key-value store used as Kubernetes' backing store for all cluster data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scaling and backups of the cluster&lt;/strong&gt; — AWS automatically handles scaling the control plane and maintains backups of your cluster state, ensuring high availability.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Patching and updating control plane components&lt;/strong&gt; — Security patches and version updates are managed by AWS, keeping your control plane secure and up-to-date.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This means you can focus on deploying your applications rather than managing Kubernetes infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use EKS?
&lt;/h2&gt;

&lt;p&gt;There are several compelling reasons to choose EKS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Operations&lt;/strong&gt; — Running and scaling Kubernetes on your own requires significant expertise. You need to manage etcd clusters, configure API servers, and handle upgrades carefully. EKS removes this complexity by providing a production-ready control plane out of the box.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt; — Kubernetes security involves network policies, RBAC, secrets management, and more. EKS integrates natively with AWS IAM for authentication, making it easier to implement least-privilege access. Your control plane runs in an AWS-managed VPC, isolated from other customers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS Integration&lt;/strong&gt; — EKS works seamlessly with services like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; for authentication and authorization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC&lt;/strong&gt; for network isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; for logging and monitoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ELB&lt;/strong&gt; for load balancing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECR&lt;/strong&gt; for container image storage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This tight integration simplifies building production-grade applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Worker Nodes: Your Options
&lt;/h2&gt;

&lt;p&gt;While EKS manages the control plane, &lt;strong&gt;you are responsible for the worker nodes&lt;/strong&gt; (where your applications actually run). There are three ways to set up worker nodes:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Self-Managed Nodes
&lt;/h3&gt;

&lt;p&gt;With self-managed nodes, you have complete control but also full responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual EC2 provisioning&lt;/strong&gt; — You select and launch EC2 instances yourself, choosing instance types, AMIs, and configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Kubernetes components&lt;/strong&gt; — All worker processes must be installed manually:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubelet&lt;/code&gt; — The primary node agent that ensures containers are running in pods&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kube-proxy&lt;/code&gt; — Maintains network rules for pod communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container runtime&lt;/strong&gt; — Software that runs containers (e.g., containerd, Docker)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Ongoing maintenance&lt;/strong&gt; — Security patches, OS updates, and Kubernetes version upgrades are your responsibility.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Node registration&lt;/strong&gt; — You must configure nodes to register with the EKS control plane.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Teams needing full control over node configuration, custom AMIs, or specific compliance requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Managed Node Groups
&lt;/h3&gt;

&lt;p&gt;Managed node groups let AWS handle the heavy lifting while you retain some control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated provisioning&lt;/strong&gt; — AWS creates and configures EC2 instances for you using EKS-optimized AMIs that come pre-configured with the necessary Kubernetes components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified lifecycle management&lt;/strong&gt; — Create, update, or terminate nodes with a single API call. AWS handles rolling updates gracefully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto Scaling integration&lt;/strong&gt; — Each node group is backed by an Auto Scaling group, allowing automatic scaling based on demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed updates&lt;/strong&gt; — AWS can update nodes to new AMI versions while respecting pod disruption budgets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Most production workloads where you want a balance of control and convenience.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AWS Fargate
&lt;/h3&gt;

&lt;p&gt;Fargate provides a fully serverless approach to running containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No node management&lt;/strong&gt; — You don't provision, configure, or scale EC2 instances. AWS handles all infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-demand compute&lt;/strong&gt; — Fargate spins up compute resources only when pods are scheduled, and removes them when pods terminate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right-sized resources&lt;/strong&gt; — Based on your pod's CPU and memory requirements, Fargate automatically provisions appropriately sized compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay-per-use pricing&lt;/strong&gt; — You only pay for the vCPU and memory your pods actually consume, billed per second.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Variable or unpredictable workloads, cost optimization, and teams wanting zero infrastructure management.&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating an EKS Cluster
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To create an EKS cluster, you need to configure several components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cluster configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cluster name&lt;/strong&gt; — A unique identifier for your cluster within the region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes version&lt;/strong&gt; — The version of Kubernetes to run (EKS supports multiple versions)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IAM role for the cluster&lt;/strong&gt; — An IAM role that grants EKS permissions to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provision nodes&lt;/strong&gt; — Create and manage EC2 instances for worker nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access storage&lt;/strong&gt; — Interact with EBS volumes and other storage services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage secrets&lt;/strong&gt; — Access AWS Secrets Manager or Systems Manager Parameter Store&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Networking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC and Subnets&lt;/strong&gt; — The network where your cluster will run. Subnets should span multiple Availability Zones for high availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security groups&lt;/strong&gt; — Firewall rules controlling traffic to and from your cluster components&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating Worker Nodes
&lt;/h3&gt;

&lt;p&gt;After creating the cluster, set up your node group:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Node Group (a group of nodes for your Kubernetes environment)&lt;/li&gt;
&lt;li&gt;Select instance type&lt;/li&gt;
&lt;li&gt;Define min/max number of nodes&lt;/li&gt;
&lt;li&gt;Specify which EKS cluster to connect to&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Methods to Create an EKS Cluster
&lt;/h2&gt;

&lt;p&gt;There are three primary ways to create an EKS cluster:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AWS Console
&lt;/h3&gt;

&lt;p&gt;The web-based approach - more steps involved but provides a visual interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;a href="https://docs.aws.amazon.com/eks/latest/eksctl/what-is-eksctl.html" rel="noopener noreferrer"&gt;eksctl CLI&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A command-line tool that simplifies cluster creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single command provisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security groups&lt;/li&gt;
&lt;li&gt;VPC&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;Node groups&lt;/li&gt;
&lt;li&gt;And more...&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Infrastructure as Code (IaC)
&lt;/h3&gt;

&lt;p&gt;Using tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;Pulumi&lt;/li&gt;
&lt;li&gt;AWS CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Reproducible, version-controlled infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hands-On Demo: Creating Your First EKS Cluster
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install eksctl
&lt;/h3&gt;

&lt;p&gt;Follow the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html" rel="noopener noreferrer"&gt;official AWS documentation&lt;/a&gt; to install eksctl OR you can use these steps on this github profile to install both &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;eksctl&lt;/code&gt;, these steps works for &lt;code&gt;Linux&lt;/code&gt;, &lt;code&gt;MacOS&lt;/code&gt; and &lt;code&gt;Windows&lt;/code&gt; &lt;a href="https://github.com/bansikah22/eks-at-scale/blob/master/setup/manual/install-tools.md" rel="noopener noreferrer"&gt;Link to github repo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Ensure AWS Authentication
&lt;/h3&gt;

&lt;p&gt;Make sure you can &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;awsctl&lt;/a&gt; installed so that you can correctly setup your credentials&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Explore eksctl Commands
&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;# Get help&lt;/span&gt;
eksctl &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="c"&gt;# Explore create options&lt;/span&gt;
eksctl create &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="c"&gt;# Explore cluster creation options&lt;/span&gt;
eksctl create cluster &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Create the Cluster
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; cluster1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodegroup-name&lt;/span&gt; ng1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-type&lt;/span&gt; t2.micro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodes&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Breakdown of Each Option
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;eksctl create cluster&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Creates a new Amazon EKS cluster and provisions the required AWS infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;-n cluster1&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Sets the name of the EKS cluster to &lt;code&gt;cluster1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--nodegroup-name ng1&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Defines the name of the managed node group as &lt;code&gt;ng1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--region us-east-1&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Specifies the AWS region where the EKS cluster will be created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--node-type t2.micro&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Chooses the EC2 instance type for worker nodes in the node group.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--nodes 2&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Sets the desired number of worker nodes in the node group to 2.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  What This Command Creates
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;An Amazon EKS control plane managed by AWS&lt;/li&gt;
&lt;li&gt;A managed EC2 node group named &lt;code&gt;ng1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Two EC2 worker nodes using the &lt;code&gt;t2.micro&lt;/code&gt; instance type&lt;/li&gt;
&lt;li&gt;Required networking resources (VPC, subnets, and security groups) if they don’t already exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait for the provisioning to complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcisb7q1quczrxhufh28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcisb7q1quczrxhufh28.png" alt="Create cluster" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It takes quite some time because it needs to create like the whole infrastructure for your, the Subnets, Node groups and so ... when it is done you will have something like this &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lmpra2lf8zxnhdj4ab7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lmpra2lf8zxnhdj4ab7.png" alt="Cluster provisioning complete" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Verify Created Resources
&lt;/h3&gt;

&lt;p&gt;After creation, check the following in AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;EKS Services&lt;/li&gt;
&lt;li&gt;Compute &amp;gt; Node Group&lt;/li&gt;
&lt;li&gt;EC2 Instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First you can search EKS and you will see that our cluster has ben created &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzkrj7rq1u0v3ht76rwb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzkrj7rq1u0v3ht76rwb.png" alt="AWS cluster eks" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and if you look further you will see the version of kubernetes we have &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq9g2tuguzceedbc6my5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq9g2tuguzceedbc6my5.png" alt="Cluster with version" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also have our Node group created with our nodes(EC2) you can see&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e6vvwhvcxo9hbcxat56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e6vvwhvcxo9hbcxat56.png" alt="Node groups compute" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you check the instances you will see that our instances are up and running ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg75wlwlgagqakzg8bj4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg75wlwlgagqakzg8bj4s.png" alt="Running nodes" width="800" height="384"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And you can also see the security group and so &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9iujtkjobmoco0j5d3cy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9iujtkjobmoco0j5d3cy.png" alt="Security group" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Connecting to Your EKS Cluster
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify kubectl Configuration
&lt;/h3&gt;

&lt;p&gt;You can verify that your &lt;code&gt;kubeconfig&lt;/code&gt; file has been updated.&lt;br&gt;&lt;br&gt;
The &lt;code&gt;kubeconfig&lt;/code&gt; file tells &lt;code&gt;kubectl&lt;/code&gt; which cluster to connect to and&lt;br&gt;
how to authenticate when communicating with the Kubernetes API server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl config view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Your Nodes
&lt;/h3&gt;

&lt;p&gt;You can also get your worker nodes directly from the terminal, and you can clearly see that using the tool &lt;code&gt;eksctl&lt;/code&gt; all the cluster creation and worker nodes connection is automatically done for you&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 nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiba1i0v56ck4nz2itm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiba1i0v56ck4nz2itm9.png" alt="Get work nodes" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see your worker nodes listed, your connection is successful!&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleaning Up
&lt;/h2&gt;

&lt;p&gt;To avoid unnecessary charges, delete your cluster when done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl delete cluster &lt;span class="nt"&gt;-n&lt;/span&gt; cluster1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32zyjil8qd2uh08eyeya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32zyjil8qd2uh08eyeya.png" alt="Delete Cluster" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this first part of the series, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What EKS is&lt;/strong&gt; - A managed Kubernetes service where AWS handles the control plane&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why use EKS&lt;/strong&gt; - Simplified operations, enhanced security, and AWS integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worker node options&lt;/strong&gt; - Self-managed, Managed Node Groups, and Fargate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster creation methods&lt;/strong&gt; - Console, eksctl, and IaC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic cluster operations&lt;/strong&gt; - Creating, connecting, and deleting clusters&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we'll dive deeper into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EKS architecture and components&lt;/li&gt;
&lt;li&gt;How to Setup an EKS cluster using terraform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/" rel="noopener noreferrer"&gt;Amazon EKS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eksctl.io/" rel="noopener noreferrer"&gt;eksctl Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/" rel="noopener noreferrer"&gt;Kubernetes Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Did you find this article helpful? Follow along with the series and drop a comment with your questions!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>eks</category>
      <category>devops</category>
    </item>
    <item>
      <title>Achieving Least Privilege in Docker</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Sat, 08 Nov 2025 11:29:35 +0000</pubDate>
      <link>https://dev.to/bansikah/achieving-least-privilege-in-docker-33je</link>
      <guid>https://dev.to/bansikah/achieving-least-privilege-in-docker-33je</guid>
      <description>&lt;p&gt;Ensuring containers run with only the permissions they truly need is one of the most effective defenses against compromise. The Docker documentation emphasises &lt;em&gt;least privilege&lt;/em&gt; as a core security practice—limit what a container can do, lower the blast radius if it is exploited, and make abuse easier to detect.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; This guide walks through key controls Docker exposes, and finishes with a repeatable hands-on exercise you can run locally.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What “Least Privilege” Means for Containers
&lt;/h2&gt;

&lt;p&gt;At its heart, least privilege is the idea that workloads only need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the minimum filesystem access required to operate&lt;/li&gt;
&lt;li&gt;a non-root identity, so the container cannot trivially take over the host&lt;/li&gt;
&lt;li&gt;only the Linux capabilities that map to the tasks it performs&lt;/li&gt;
&lt;li&gt;explicit resource limits to contain runaway processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker exposes controls for every one of these dimensions through both the &lt;code&gt;Dockerfile&lt;/code&gt; (build-time choices) and &lt;code&gt;docker run&lt;/code&gt; flags (runtime policy).&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Build-Time Controls (Dockerfile)
&lt;/h2&gt;

&lt;p&gt;The official guidelines highlight three quick wins:&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use a minimal base image&lt;/strong&gt; (e.g., &lt;code&gt;distroless&lt;/code&gt; or Alpine) so the container inherits fewer default binaries and attack surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create and switch to a non-root user&lt;/strong&gt; with only the filesystem paths it needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope file ownership and permissions&lt;/strong&gt; so write access is limited.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;

&lt;span class="c"&gt;# Create a system user and group&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--ingroup&lt;/span&gt; app app

&lt;span class="c"&gt;# Copy application code and drop ownership to the app user&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/service&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=app:app requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=app:app . .&lt;/span&gt;

&lt;span class="c"&gt;# Run as the non-root user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;USER&lt;/code&gt; directive ensures all subsequent layers (and the running process) inherit the non-root identity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--chown&lt;/code&gt; on &lt;code&gt;COPY&lt;/code&gt; prevents root-owned artefacts, which could otherwise allow privilege escalation if the runtime tried to write to those files.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Runtime Controls (&lt;code&gt;docker run&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Even with a hardened image, Docker defaults to granting capabilities beyond what most applications need. Official docs recommend &lt;em&gt;dropping all capabilities by default&lt;/em&gt;, then re-adding only the ones the workload relies on.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Here are the runtime flags we will use in the practical section:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overrides the container’s user ID at runtime. Using the same non-root ID as the Dockerfile keeps both layers consistent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--cap-drop ALL&lt;/code&gt; and &lt;code&gt;--cap-add&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Remove every capability, then add back a handful—for example &lt;code&gt;NET_BIND_SERVICE&lt;/code&gt; for binding to privileged ports.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--read-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount the container filesystem as read-only; pair with writable volumes for state that must persist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--tmpfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provide ephemeral writable space (e.g., &lt;code&gt;/tmp&lt;/code&gt;) without broad write access elsewhere.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--security-opt no-new-privileges&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocks setuid binaries and other mechanisms from elevating privilege inside the container.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--memory&lt;/code&gt;, &lt;code&gt;--cpus&lt;/code&gt;, &lt;code&gt;--pids-limit&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Resource governance—if the process is compromised it cannot starve the host.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Hands-On: Locking Down a Simple Web Service
&lt;/h2&gt;

&lt;p&gt;The following example runs a tiny Flask service with progressively tighter permissions. You can adapt it to your own applications by swapping out the base image and command.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Prepare the demo
&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;cat&lt;/span&gt; &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;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Least privilege demo!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &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;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; requirements.txt
flask==3.0.2
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &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;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; Dockerfile
FROM python:3.12-slim

RUN addgroup --system app &amp;amp;&amp;amp; adduser --system --ingroup app app
WORKDIR /opt/service
COPY --chown=app:app requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=app:app app.py .

USER app
CMD ["python", "app.py"]
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; least-privilege-demo:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Run with default privileges (baseline)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 least-privilege-demo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but the container still has many capabilities and a writable root filesystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Re-run with least privilege
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; least-priv &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-drop&lt;/span&gt; ALL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt; NET_BIND_SERVICE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--security-opt&lt;/span&gt; no-new-privileges &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt; 256m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt; 0.5 &lt;span class="se"&gt;\&lt;/span&gt;
  least-privilege-demo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What changed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The filesystem is now read-only, except for the &lt;code&gt;tmpfs&lt;/code&gt; mount at &lt;code&gt;/tmp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Capabilities are stripped down to one—the app can bind to port 5000 (above 1024), so technically it would work even without &lt;code&gt;NET_BIND_SERVICE&lt;/code&gt;, but this illustrates how to add back the minimum set.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no-new-privileges&lt;/code&gt; ensures setuid binaries cannot escalate.&lt;/li&gt;
&lt;li&gt;Resource limits prevent denial-of-service side effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify the container is healthy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:5000
docker &lt;span class="nb"&gt;exec &lt;/span&gt;least-priv &lt;span class="nb"&gt;id
&lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;least-priv &lt;span class="nb"&gt;cat&lt;/span&gt; /proc/self/status | &lt;span class="nb"&gt;grep &lt;/span&gt;CapEff
docker &lt;span class="nb"&gt;exec &lt;/span&gt;least-priv &lt;span class="nb"&gt;touch&lt;/span&gt; /tmp/test-file &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;exec &lt;/span&gt;least-priv &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /tmp/test-file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:5000
Least privilege demo!

$ docker exec least-priv id
uid=100(app) gid=101(app) groups=101(app)

$ docker exec least-priv cat /proc/self/status | grep CapEff
CapEff: 0000000000000000

$ docker exec least-priv ls -l /tmp/test-file
-rw-r--r-- 1 app app 0 Nov  8 08:04 /tmp/test-file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The zeroed-out &lt;code&gt;CapEff&lt;/code&gt; confirms all capabilities are dropped. Trying to write anywhere other than &lt;code&gt;/tmp&lt;/code&gt; should fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;least-priv &lt;span class="nb"&gt;touch&lt;/span&gt; /opt/service/newfile
&lt;span class="c"&gt;# touch: cannot touch '/opt/service/newfile': Read-only file system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Clean up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; least-priv
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile app.py requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Additional Hardening Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rootless Docker:&lt;/strong&gt; Run the Docker daemon itself without root privileges.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated AppArmor/SELinux Profiles:&lt;/strong&gt; Limit system calls and file access even further.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only Secrets:&lt;/strong&gt; Mount configuration through Docker secrets or Kubernetes ConfigMaps and avoid baking secrets into images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supply chain controls:&lt;/strong&gt; Pin base image digests and verify them before promotion.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Least privilege is a principle; Docker gives you the tooling to apply it.&lt;/li&gt;
&lt;li&gt;Small, non-root images eliminate entire classes of privilege escalation.&lt;/li&gt;
&lt;li&gt;Runtime flags let you defend in depth—capabilities, filesystem access, resource limits.&lt;/li&gt;
&lt;li&gt;The pattern mirrors Kubernetes security contexts, so what you practice locally carries over to orchestrated environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these habits, containers remain focused on the work they should do—and little else.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Docker Docs — &lt;a href="https://docs.docker.com/build/building/best-practices/#security" rel="noopener noreferrer"&gt;Apply security best practices to Dockerfiles&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/security/rootless/" rel="noopener noreferrer"&gt;Runtime privilege &amp;amp; Linux capabilities&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Docker Docs — &lt;a href="https://docs.docker.com/engine/security/capabilities/" rel="noopener noreferrer"&gt;Linux capabilities in practice&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Docker Docs — &lt;a href="https://docs.docker.com/engine/security/rootless/" rel="noopener noreferrer"&gt;Run the Docker daemon as a non-root user (Rootless mode)&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>container</category>
      <category>security</category>
      <category>python</category>
    </item>
    <item>
      <title>Building MCP Servers: Understanding Transport Layers and Core Components</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Tue, 16 Sep 2025 21:20:26 +0000</pubDate>
      <link>https://dev.to/bansikah/building-mcp-servers-understanding-transport-layers-and-core-components-3o1o</link>
      <guid>https://dev.to/bansikah/building-mcp-servers-understanding-transport-layers-and-core-components-3o1o</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/bansikah/mcp-basics-understanding-the-model-context-protocol-566g"&gt;previous article&lt;/a&gt;, we explored what the Model Context Protocol (MCP) is and why it's revolutionizing how AI models interact with external systems. Now that we understand MCP's potential, let's dive deep into the implementation details—how to actually build MCP servers that are robust, scalable, and secure.&lt;/p&gt;

&lt;p&gt;Building effective MCP servers requires mastering two key areas: the transport mechanisms that enable communication between clients and servers, and the core components (tools, resources, and prompts) that define your server's functionality. Let's explore each in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transport Mechanisms: How MCP Connects
&lt;/h2&gt;

&lt;p&gt;Think of transport mechanisms as the "delivery methods" for messages between AI models and your MCP server. Just like choosing how to send a package—hand delivery for urgent local items, postal service for standard shipping, or express courier for time-sensitive deliveries—MCP offers different transport options optimized for specific scenarios.&lt;/p&gt;

&lt;p&gt;Imagine you're running a restaurant (your MCP server) and customers (AI models) want to place orders. You could take orders through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct conversation&lt;/strong&gt; (STDIO) - fastest, but only one customer at a time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phone calls&lt;/strong&gt; (HTTP) - can handle multiple customers, works remotely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live updates on a screen&lt;/strong&gt; (SSE) - great for showing order status in real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The transport layer handles all the communication logistics—message formatting, delivery confirmation, error handling—so you can focus on cooking (your server's business logic) rather than managing the ordering system.&lt;/p&gt;

&lt;h3&gt;
  
  
  STDIO Transport: Direct Process Communication
&lt;/h3&gt;

&lt;p&gt;STDIO transport is like having a private, dedicated phone line between the AI model and your server. When Claude Desktop wants to use your MCP server, it's like hiring a personal assistant (spawning your server process) who sits right next to them and communicates through simple notes passed back and forth.&lt;/p&gt;

&lt;p&gt;Think of it as the difference between shouting across a crowded room versus having a quiet, one-on-one conversation. There's no network noise, no interference from other conversations, and no delay—just direct, immediate communication. This is why STDIO is perfect for desktop applications where speed and reliability matter most.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server setup with STDIO transport&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-mcp-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioServerTransport&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&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;Technical Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: JSON-RPC over stdin/stdout streams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Model&lt;/strong&gt;: Client spawns server as child process&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Ultra-low (~1ms) - no network overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;: Single client per server instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection&lt;/strong&gt;: Automatic cleanup when client terminates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use STDIO:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building tools for Claude Desktop or similar local AI apps&lt;/li&gt;
&lt;li&gt;Creating development utilities that need fast response times&lt;/li&gt;
&lt;li&gt;When you want simple deployment (just an executable file)&lt;/li&gt;
&lt;li&gt;For personal productivity tools on your local machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Scenario:&lt;/strong&gt; You're building a code analysis tool for developers. Using STDIO transport means Claude Desktop can spawn your server instantly when needed, analyze code with minimal delay, and automatically clean up when done.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Transport: Web-Scale Communication
&lt;/h3&gt;

&lt;p&gt;HTTP transport is like turning your MCP server into a popular restaurant that takes phone orders. Instead of having one dedicated assistant (STDIO), you now have a phone system that can handle multiple customers calling simultaneously from anywhere in the world.&lt;/p&gt;

&lt;p&gt;Just like a restaurant can serve customers who call from different locations, HTTP transport allows multiple AI clients to connect to your server over the internet. You can set up security (like requiring a password to place orders), handle busy periods with load balancing (multiple phone lines), and even serve customers in different time zones. The trade-off is slightly slower service due to the "phone call overhead," but you gain the ability to serve many more customers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HTTP server implementation&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/http.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-mcp-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpServerTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&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;Technical Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: JSON-RPC over HTTP/HTTPS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection Model&lt;/strong&gt;: Stateless request-response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Network-dependent (typically 10-100ms+ depending on distance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;: Handles multiple simultaneous clients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling&lt;/strong&gt;: Can use load balancers, reverse proxies, clustering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use HTTP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remote Access&lt;/strong&gt;: Server and client are on different machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Integration&lt;/strong&gt;: Embedding MCP functionality in web applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Sharing&lt;/strong&gt;: Multiple developers need access to the same server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Deployment&lt;/strong&gt;: Running servers on AWS, Google Cloud, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Systems&lt;/strong&gt;: Need monitoring, logging, and enterprise features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Scenario:&lt;/strong&gt; Your team needs a shared database query tool. Deploy it as an HTTP MCP server on your cloud infrastructure, and multiple team members can access it remotely through their AI clients. Add authentication headers to control access and use HTTPS for secure communication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Sent Events (SSE): Real-Time Streaming
&lt;/h3&gt;

&lt;p&gt;SSE transport is like having a live sports commentator giving play-by-play updates. Instead of the AI having to repeatedly ask "Are you done yet?" (like checking order status every few minutes), your server can actively broadcast updates as things happen.&lt;/p&gt;

&lt;p&gt;Imagine you're processing a large dataset—with SSE, you can send updates like "10% complete... 25% complete... found interesting pattern... 75% complete..." This keeps the AI informed and engaged, rather than leaving it waiting in silence. It's perfect for long-running tasks where you want to show progress, like a pizza tracker showing when your order is being prepared, baked, and out for delivery.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SSE transport for real-time updates&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SseServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/sse.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SseServerTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Stream real-time updates&lt;/span&gt;
&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendEvent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&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;Technical Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: Server-Sent Events over HTTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection Model&lt;/strong&gt;: Long-lived, server-to-client streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Near real-time (typically &amp;lt;100ms for event delivery)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;: Multiple clients can subscribe to event streams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Built-in reconnection and event ID tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use SSE:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long Operations&lt;/strong&gt;: File processing, data analysis, model training&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Updates&lt;/strong&gt;: Real-time dashboards, monitoring systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress Tracking&lt;/strong&gt;: Show completion status for multi-step workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Notifications&lt;/strong&gt;: Alert clients about system changes or updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Scenario:&lt;/strong&gt; You're building an AI-powered data processing pipeline. Use SSE to stream progress updates as files are processed, allowing the AI client to provide real-time feedback to users and make decisions based on intermediate results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transport Selection Guide
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transport&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Scalability&lt;/th&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Security&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;STDIO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1ms&lt;/td&gt;
&lt;td&gt;Single client&lt;/td&gt;
&lt;td&gt;Executable file&lt;/td&gt;
&lt;td&gt;Process isolation&lt;/td&gt;
&lt;td&gt;Local tools, Claude Desktop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10-100ms+&lt;/td&gt;
&lt;td&gt;High (load balancing)&lt;/td&gt;
&lt;td&gt;Web server + networking&lt;/td&gt;
&lt;td&gt;HTTPS + auth headers&lt;/td&gt;
&lt;td&gt;Remote access, web apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;100ms&lt;/td&gt;
&lt;td&gt;Medium (connection limits)&lt;/td&gt;
&lt;td&gt;Web server + event handling&lt;/td&gt;
&lt;td&gt;HTTP security + validation&lt;/td&gt;
&lt;td&gt;Real-time updates, monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose STDIO when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server will run on the same machine as the AI client&lt;/li&gt;
&lt;li&gt;You need the fastest possible response times&lt;/li&gt;
&lt;li&gt;You want simple deployment (single executable)&lt;/li&gt;
&lt;li&gt;Building for Claude Desktop or similar local applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose HTTP when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need remote access (server and client on different machines)&lt;/li&gt;
&lt;li&gt;Multiple clients need to use the same server simultaneously
&lt;/li&gt;
&lt;li&gt;You're integrating with web applications or cloud services&lt;/li&gt;
&lt;li&gt;You need enterprise features like load balancing and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose SSE when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your operations take significant time and need progress updates&lt;/li&gt;
&lt;li&gt;You're building real-time monitoring or dashboard systems&lt;/li&gt;
&lt;li&gt;You need to push notifications or alerts to clients&lt;/li&gt;
&lt;li&gt;Your workflow involves streaming data or continuous updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Core Components: The Building Blocks
&lt;/h1&gt;

&lt;p&gt;Think of MCP servers like a Swiss Army knife for AI models. Just as a Swiss Army knife has different tools for different tasks, MCP servers have three types of components that give AI models different capabilities. Understanding these components is like learning what each tool in the knife does and when to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools: Executable Actions
&lt;/h2&gt;

&lt;p&gt;Tools are like the power tools in a workshop—they're what allow the AI to actually &lt;em&gt;do&lt;/em&gt; things in the real world. Just as a carpenter has different tools for different jobs (saw for cutting, drill for holes, sander for smoothing), your MCP server provides different tools for different actions.&lt;/p&gt;

&lt;p&gt;Think of tools as the AI's "hands"—they extend the AI's capabilities beyond just thinking and talking. Want to search through files? There's a tool for that. Need to send an email? Another tool. Process an image? Yet another tool.&lt;/p&gt;

&lt;p&gt;The magic happens when you design tools like a good craftsperson organizes their workshop: each tool has a clear purpose, is reliable when used correctly, and comes with clear instructions (documentation) so even a novice can use it safely and effectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tool definition with comprehensive validation&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ListToolsRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tools&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search_files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search for files using glob patterns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Glob pattern (e.g., "*.ts", "**/*.json")&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^[^/&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s1"&gt;:*?"&amp;lt;&amp;gt;|]+$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Security: prevent path traversal&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;maxResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;minimum&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="na"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pattern&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// Tool execution with error handling&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CallToolRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&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="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search_files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleSearchFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validateArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unknown tool: &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;`&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;isError&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="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;Tool Design Principles:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1  &lt;strong&gt;Single Responsibility&lt;/strong&gt;: Each tool should do one thing well&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Good: &lt;code&gt;search_files&lt;/code&gt; - searches for files using patterns&lt;/li&gt;
&lt;li&gt;❌ Bad: &lt;code&gt;file_manager&lt;/code&gt; - searches, creates, deletes, and modifies files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2  &lt;strong&gt;Input Validation&lt;/strong&gt;: Always validate and sanitize inputs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Validate file patterns to prevent security issues&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9*.&lt;/span&gt;&lt;span class="se"&gt;\-&lt;/span&gt;&lt;span class="sr"&gt;_&lt;/span&gt;&lt;span class="se"&gt;/]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid pattern: contains unsafe characters&lt;/span&gt;&lt;span class="dl"&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;3  &lt;strong&gt;Error Handling&lt;/strong&gt;: Provide clear, actionable error messages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Instead of: "Error occurred"&lt;/span&gt;
   &lt;span class="c1"&gt;// Use: "File not found: /path/to/file.txt. Check the path and permissions."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4  &lt;strong&gt;Rich Documentation&lt;/strong&gt;: Help AI models understand when and how to use tools&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search for files using glob patterns. Use * for wildcards, ** for recursive search. Example: "**/*.js" finds all JavaScript files recursively.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Technical Implementation Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools use JSON Schema for input validation, providing type safety and automatic validation&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;inputSchema&lt;/code&gt; property defines exactly what parameters the tool accepts&lt;/li&gt;
&lt;li&gt;Return values should always include a &lt;code&gt;content&lt;/code&gt; array with structured responses&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;isError&lt;/code&gt; flag to indicate when operations fail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources: Accessible Data
&lt;/h2&gt;

&lt;p&gt;Resources are like a well-organized library that the AI can browse and read from. Just as a library has books, magazines, DVDs, and digital resources all organized with a clear cataloging system (like the Dewey Decimal System), MCP resources use URI patterns to organize different types of information.&lt;/p&gt;

&lt;p&gt;Think of resources as the AI's "reference materials." When a student needs to write a research paper, they don't just need tools (pen, computer, printer)—they need access to information (books, articles, databases). Similarly, AI models need both tools to take actions AND resources to understand context and make informed decisions.&lt;/p&gt;

&lt;p&gt;The URI system works like library call numbers: &lt;code&gt;file://documents/readme.md&lt;/code&gt; is like saying "go to the Documents section, find the README file," while &lt;code&gt;api://users/123/profile&lt;/code&gt; means "check the User Records section, look up person #123's profile." This consistent addressing system helps the AI quickly find exactly what it needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Resource listing with metadata&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ListResourcesRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;resources&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="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://documents/readme.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Project README&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main project documentation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/markdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api://users/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User Profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Current user profile data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;// Resource reading with content negotiation&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ReadResourceRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&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="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unsupported resource URI: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;Resource Design Patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1  &lt;strong&gt;URI Schemes&lt;/strong&gt;: Create consistent, hierarchical patterns&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// File system resources&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://documents/readme.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://logs/2024/january/app.log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

   &lt;span class="c1"&gt;// API resources  &lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api://users/123/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api://orders/456/items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

   &lt;span class="c1"&gt;// Database resources&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db://customers/active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db://products/category/electronics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2  &lt;strong&gt;MIME Types&lt;/strong&gt;: Enable proper content handling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Let AI clients know what type of data they're getting&lt;/span&gt;
   &lt;span class="nx"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;     &lt;span class="c1"&gt;// Structured data&lt;/span&gt;
   &lt;span class="nx"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/markdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// Documentation&lt;/span&gt;
   &lt;span class="nx"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;            &lt;span class="c1"&gt;// Tabular data&lt;/span&gt;
   &lt;span class="nx"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;           &lt;span class="c1"&gt;// Binary content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3  &lt;strong&gt;Caching Strategy&lt;/strong&gt;: Implement efficient updates&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Use ETags to avoid re-reading unchanged resources&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateETag&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if-none-match&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;304&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Not modified&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4  &lt;strong&gt;Security Considerations&lt;/strong&gt;: Always validate access&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Prevent path traversal attacks&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;safePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SAFE_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestedPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;safePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SAFE_ROOT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access denied: path outside allowed directory&lt;/span&gt;&lt;span class="dl"&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;Technical Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resources are read-only from the AI client's perspective&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;ListResourcesRequestSchema&lt;/code&gt; to advertise available resources&lt;/li&gt;
&lt;li&gt;Implement &lt;code&gt;ReadResourceRequestSchema&lt;/code&gt; to serve resource content&lt;/li&gt;
&lt;li&gt;Support content negotiation through MIME types for optimal AI processing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prompts: Conversation Templates
&lt;/h2&gt;

&lt;p&gt;Prompts are like having a collection of conversation scripts or templates that help guide interactions. Think of them as the "Mad Libs" of AI conversations—you create a template with blanks to fill in, and the AI can use these templates to start productive conversations.&lt;/p&gt;

&lt;p&gt;Imagine you're a manager who frequently needs to conduct different types of meetings. Instead of improvising each time, you might have templates: "Performance Review Template," "Project Kickoff Template," "Problem-Solving Template." Each template has a structure and key questions, but you fill in the specific details for each situation.&lt;/p&gt;

&lt;p&gt;MCP prompts work the same way—they provide proven conversation patterns that AI models can use as starting points, customized with specific context for each situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompts use template variables for dynamic content generation&lt;/li&gt;
&lt;li&gt;They return complete conversation contexts with role-based messages&lt;/li&gt;
&lt;li&gt;Parameters enable customization without changing the core template&lt;/li&gt;
&lt;li&gt;The AI client invokes prompts to establish structured conversation flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use Prompts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardizing Workflows&lt;/strong&gt;: Create consistent approaches to common tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Expertise&lt;/strong&gt;: Embed specialized knowledge into conversation starters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Processes&lt;/strong&gt;: Guide AI through multi-step procedures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality Control&lt;/strong&gt;: Ensure AI interactions follow best practices
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prompt definition with variables&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ListPromptsRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;prompts&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generate code review comments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;arguments&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Programming language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;required&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="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus_areas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Areas to focus on (security, performance, etc.)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;required&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="p"&gt;]&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="c1"&gt;// Prompt rendering with context injection&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setRequestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GetPromptRequestSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&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="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusAreas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;focus_areas&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;general best practices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Code review for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Please review this &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; code focusing on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;focusAreas&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. 
                   Provide specific, actionable feedback with examples.`&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="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unknown prompt: &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;`&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;h2&gt;
  
  
  Integration Patterns: Making Components Work Together
&lt;/h2&gt;

&lt;p&gt;The real power of MCP comes from how these components work together, like instruments in an orchestra. Each component has its role, but the magic happens when they're coordinated to create something greater than the sum of their parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool-Resource Synergy
&lt;/h3&gt;

&lt;p&gt;Think of the relationship between tools and resources like a detective and evidence. The detective (AI) uses tools (investigation methods) to gather and analyze resources (evidence, witness statements, documents). Sometimes using a tool creates new evidence that becomes a resource for further investigation.&lt;/p&gt;

&lt;p&gt;Tools and resources work together to create powerful workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tool that creates resources dynamically&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleAnalyzeCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Tool action: analyze the code&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analysis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;analyzeCodeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a resource with results&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resourceUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`analysis://reports/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;resourceCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&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;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;analysis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Analysis complete. View results at: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;Real-World Example:&lt;/strong&gt;&lt;br&gt;
Imagine building a code review assistant. The AI uses the &lt;code&gt;analyze_code&lt;/code&gt; tool to examine a file, which creates an analysis resource. Then it uses the &lt;code&gt;read_resource&lt;/code&gt; capability to access the detailed analysis, and finally uses a &lt;code&gt;generate_report&lt;/code&gt; tool to create a formatted review document. Each step builds on the previous one, creating a powerful workflow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prompt-Driven Workflows
&lt;/h3&gt;

&lt;p&gt;Prompts act like a GPS for complex tasks—they provide step-by-step directions to help the AI navigate from problem to solution. Just as GPS breaks down a long journey into manageable turns ("In 500 feet, turn left, then continue straight for 2 miles"), prompts break down complex workflows into clear, actionable steps.&lt;/p&gt;

&lt;p&gt;Prompts can guide AI models through complex multi-step processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Multi-step workflow prompt&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workflowPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug_issue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;arguments&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error_message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&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="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a debugging expert. Follow systematic troubleshooting procedures.&lt;/span&gt;&lt;span class="dl"&gt;'&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Debug this &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; issue: "&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;"

               Follow this systematic approach:
               1. Use search_files to find relevant code files
               2. Use read_file to examine the problematic code
               3. Use get_logs to check for related errors
               4. Use check_dependencies to verify system state
               5. Provide a diagnosis with specific fix recommendations`&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Practical Application:&lt;/strong&gt;&lt;br&gt;
When a developer encounters a bug, they can invoke the &lt;code&gt;debug_issue&lt;/code&gt; prompt with the error message. The AI follows the structured workflow, systematically using different tools to investigate, and provides a comprehensive diagnosis. This ensures consistent, thorough debugging every time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Performance and Security: Building for the Real World
&lt;/h2&gt;

&lt;p&gt;Building an MCP server is like constructing a building—you need both a solid foundation (security) and efficient systems (performance) to handle real-world usage. Just as architects consider both structural integrity and energy efficiency, MCP developers must balance security and performance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Performance optimization is like organizing your kitchen for efficiency. Just as a chef keeps frequently used ingredients within easy reach and pre-prepares common components, your MCP server should cache expensive operations and optimize for common use patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Implement caching for expensive operations&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cachedOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// 5 min cache&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&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="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&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="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&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;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;p&gt;Security in MCP servers is like being a careful bouncer at an exclusive club. You need to check IDs (validate inputs), ensure people don't wander into restricted areas (prevent path traversal), and maintain order while still providing good service to legitimate guests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input validation and sanitization&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateFilePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Prevent path traversal&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid path: path traversal detected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Ensure path is within allowed directory&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ALLOWED_ROOT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;resolved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ALLOWED_ROOT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access denied: path outside allowed directory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resolved&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;h2&gt;
  
  
  Putting It All Together: A Practical Example
&lt;/h2&gt;

&lt;p&gt;Let's walk through building a complete MCP server for a development team's needs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: Your team needs an AI assistant that can help with code reviews, manage project documentation, and monitor system health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transport Decision&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;HTTP transport&lt;/strong&gt; because multiple developers need access&lt;/li&gt;
&lt;li&gt;Deploy on your internal cloud infrastructure&lt;/li&gt;
&lt;li&gt;Enable HTTPS with team authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Design&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;1  &lt;strong&gt;Tools&lt;/strong&gt; (Actions the AI can perform):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Code analysis&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;analyze_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;examines&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;security&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run_tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;executes&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="nx"&gt;suites&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deploy_staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;deploys&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;staging&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;

   &lt;span class="c1"&gt;// Documentation&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update_docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;modifies&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="nx"&gt;documentation&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generate_changelog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;creates&lt;/span&gt; &lt;span class="nx"&gt;release&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;

   &lt;span class="c1"&gt;// Monitoring  &lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;check_system_health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;monitors&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get_error_logs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;retrieves&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2  &lt;strong&gt;Resources&lt;/strong&gt; (Information the AI can access):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// Code and documentation&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://src/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file://docs/**/*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;documentation&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;

   &lt;span class="c1"&gt;// System data&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api://monitoring/metrics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api://logs/errors/recent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;recent&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db://deployments/history&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;deployment&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3  &lt;strong&gt;Prompts&lt;/strong&gt; (Structured workflows):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code_review_checklist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;systematic&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;review&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;incident_response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;structured&lt;/span&gt; &lt;span class="nx"&gt;incident&lt;/span&gt; &lt;span class="nx"&gt;handling&lt;/span&gt;
   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;release_preparation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt; &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Developers can ask the AI to "review this pull request" (uses code_review_checklist prompt), "check if the system is healthy" (uses monitoring tools and resources), or "prepare for release" (follows release_preparation workflow).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building effective MCP servers is like designing a well-equipped workshop—you need the right tools for the job, organized materials you can easily find, and proven procedures for complex tasks. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transport choice matters&lt;/strong&gt;: STDIO for local speed, HTTP for remote access, SSE for real-time updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for clarity&lt;/strong&gt;: Each tool should have a single, clear purpose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organize systematically&lt;/strong&gt;: Use consistent URI patterns for resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guide with prompts&lt;/strong&gt;: Provide structured workflows for complex tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure by default&lt;/strong&gt;: Always validate inputs and control access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize for real use&lt;/strong&gt;: Cache expensive operations and monitor performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start simple with a few essential tools and resources, then expand based on actual usage patterns. The best MCP servers solve real problems elegantly rather than trying to do everything at once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/iinsys/ai-mcp" rel="noopener noreferrer"&gt;Visit this repository to learn more about MCP servers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Kubernetes Security Context: A Comprehensive Guide</title>
      <dc:creator>Tandap Noel Bansikah</dc:creator>
      <pubDate>Thu, 11 Sep 2025 19:39:51 +0000</pubDate>
      <link>https://dev.to/bansikah/kubernetes-security-context-a-comprehensive-guide-5b5f</link>
      <guid>https://dev.to/bansikah/kubernetes-security-context-a-comprehensive-guide-5b5f</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What is Security Context?&lt;/li&gt;
&lt;li&gt;Security Context Fields&lt;/li&gt;
&lt;li&gt;Pod-level vs Container-level Security Context&lt;/li&gt;
&lt;li&gt;Practical Examples&lt;/li&gt;
&lt;li&gt;Security Context Constraints&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Security is a critical aspect of container orchestration, and Kubernetes provides robust mechanisms to enforce security policies at the pod and container level. One of the most important features for container security is the &lt;strong&gt;Security Context&lt;/strong&gt;, which allows you to define privilege and access control settings for pods and containers.&lt;/p&gt;

&lt;p&gt;This article provides a comprehensive overview of Kubernetes Security Context, covering both theoretical concepts and practical implementations based on official Kubernetes documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Security Context?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Security Context&lt;/strong&gt; defines privilege and access control settings for a Pod or Container. It includes settings such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User ID (UID) and Group ID (GID) for running processes&lt;/li&gt;
&lt;li&gt;Linux capabilities&lt;/li&gt;
&lt;li&gt;SELinux options&lt;/li&gt;
&lt;li&gt;AppArmor profiles&lt;/li&gt;
&lt;li&gt;Seccomp profiles&lt;/li&gt;
&lt;li&gt;File system permissions&lt;/li&gt;
&lt;li&gt;Privilege escalation controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security contexts help enforce the principle of least privilege by ensuring containers run with only the minimum permissions necessary to function correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Context Fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Security Context Fields
&lt;/h3&gt;

&lt;p&gt;Based on the &lt;a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#securitycontext-v1-core" rel="noopener noreferrer"&gt;official Kubernetes documentation&lt;/a&gt;, here are the key fields available in a Security Context:&lt;/p&gt;

&lt;h4&gt;
  
  
  User and Group Settings
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runAsUser&lt;/code&gt;: Specifies the user ID to run the container processes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runAsGroup&lt;/code&gt;: Specifies the primary group ID for container processes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runAsNonRoot&lt;/code&gt;: Indicates that the container must run as a non-root user&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fsGroup&lt;/code&gt;: Defines a file system group ID for volume ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Privilege Controls
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;privileged&lt;/code&gt;: Determines if the container runs in privileged mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowPrivilegeEscalation&lt;/code&gt;: Controls whether a process can gain more privileges&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readOnlyRootFilesystem&lt;/code&gt;: Mounts the container's root filesystem as read-only&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Linux Security Modules
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;seLinuxOptions&lt;/code&gt;: SELinux security context settings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appArmorProfile&lt;/code&gt;: AppArmor profile configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seccompProfile&lt;/code&gt;: Seccomp profile settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Capabilities
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;capabilities&lt;/code&gt;: Linux capabilities to add or drop&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pod-level vs Container-level Security Context
&lt;/h2&gt;

&lt;p&gt;Security Context can be specified at two levels:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pod-level Security Context
&lt;/h3&gt;

&lt;p&gt;Applied to all containers in the pod (unless overridden at container level):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&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;security-context-demo&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
    &lt;span class="na"&gt;runAsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2000&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;sec-ctx-demo&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;busybox:1.28&lt;/span&gt;
    &lt;span class="na"&gt;command&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;sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1h"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Container-level Security Context
&lt;/h3&gt;

&lt;p&gt;Applied to specific containers and overrides pod-level settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&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;security-context-demo-2&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;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;sec-ctx-demo&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;busybox:1.28&lt;/span&gt;
    &lt;span class="na"&gt;command&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;sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1h"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2000&lt;/span&gt;
      &lt;span class="na"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;

&lt;p&gt;Let's explore various Security Context configurations with practical examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Basic User and Group Configuration
&lt;/h3&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;Pod&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;security-context-demo-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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
    &lt;span class="na"&gt;runAsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2000&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;sec-ctx-vol&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;busybox:1.28&lt;/span&gt;
    &lt;span class="na"&gt;command&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;sh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1h"&lt;/span&gt; &lt;span class="pi"&gt;]&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;sec-ctx-vol&lt;/span&gt;
      &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data/demo&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;sec-ctx-vol&lt;/span&gt;
    &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Runs the container process as user ID 1000&lt;/li&gt;
&lt;li&gt;Sets the primary group ID to 3000&lt;/li&gt;
&lt;li&gt;Sets the file system group to 2000 (volumes will be owned by this group)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 2: Non-Root User Enforcement
&lt;/h3&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;Pod&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;security-context-demo-2&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runAsNonRoot&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;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&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;sec-ctx-demo&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;gcr.io/google-samples/node-hello:1.0&lt;/span&gt;
    &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;readOnlyRootFilesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Ensures the container cannot run as root&lt;/li&gt;
&lt;li&gt;Prevents privilege escalation&lt;/li&gt;
&lt;li&gt;Mounts the root filesystem as read-only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 3: Capabilities Management
&lt;/h3&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;Pod&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;security-context-demo-3&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;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;sec-ctx-demo&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;gcr.io/google-samples/node-hello:1.0&lt;/span&gt;
    &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;add&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;NET_ADMIN"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SYS_TIME"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;drop&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;ALL"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Drops all default capabilities&lt;/li&gt;
&lt;li&gt;Adds only NET_ADMIN and SYS_TIME capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example 4: SELinux Configuration
&lt;/h3&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;Pod&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;security-context-demo-4&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;seLinuxOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s0:c123,c456"&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;sec-ctx-demo&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;gcr.io/google-samples/node-hello:1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 5: Seccomp Profile
&lt;/h3&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;Pod&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;security-context-demo-5&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;seccompProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RuntimeDefault&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;sec-ctx-demo&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;gcr.io/google-samples/node-hello:1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 6: Complete Security Hardening
&lt;/h3&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;Pod&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;hardened-pod&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runAsNonRoot&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;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10001&lt;/span&gt;
    &lt;span class="na"&gt;runAsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10001&lt;/span&gt;
    &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10001&lt;/span&gt;
    &lt;span class="na"&gt;seccompProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RuntimeDefault&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;hardened-container&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;gcr.io/google-samples/node-hello:1.0&lt;/span&gt;
    &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;readOnlyRootFilesystem&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;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;drop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&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;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;64Mi"&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50m"&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;tmp-volume&lt;/span&gt;
      &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp&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;tmp-volume&lt;/span&gt;
    &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Context Constraints
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pod Security Standards
&lt;/h3&gt;

&lt;p&gt;Kubernetes provides Pod Security Standards that work with Security Context:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privileged&lt;/strong&gt;: Unrestricted policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline&lt;/strong&gt;: Minimally restrictive policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restricted&lt;/strong&gt;: Heavily restricted policy&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Pod Security Policy (Deprecated in favor of Pod Security Standards)
&lt;/h3&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;policy/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;PodSecurityPolicy&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;restricted&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;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;requiredDropCapabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ALL&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;configMap'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;emptyDir'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;projected'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secret'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;downwardAPI'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;persistentVolumeClaim'&lt;/span&gt;
  &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MustRunAsNonRoot'&lt;/span&gt;
  &lt;span class="na"&gt;seLinux&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RunAsAny'&lt;/span&gt;
  &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RunAsAny'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Always Run as Non-Root
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runAsNonRoot&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;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Drop All Capabilities by Default
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;drop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use Read-Only Root Filesystem
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;readOnlyRootFilesystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Disable Privilege Escalation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Use Seccomp Profiles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;seccompProfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RuntimeDefault&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Set Resource Limits
&lt;/h3&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="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&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;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;64Mi"&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50m"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Permission Denied Errors
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Container fails to start due to permission issues&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Check &lt;code&gt;runAsUser&lt;/code&gt;, &lt;code&gt;runAsGroup&lt;/code&gt;, and &lt;code&gt;fsGroup&lt;/code&gt; settings&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt;
kubectl describe pod &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Capability Issues
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Application requires specific capabilities&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Add required capabilities while dropping unnecessary ones&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;add&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;NET_BIND_SERVICE"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;drop&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;ALL"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. File System Access Issues
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Cannot write to mounted volumes&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Set appropriate &lt;code&gt;fsGroup&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;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debugging Commands
&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;# Check pod security context&lt;/span&gt;
kubectl get pod &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 securityContext

&lt;span class="c"&gt;# Exec into pod to check user/group&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;

&lt;span class="c"&gt;# Check file permissions&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /path/to/volume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Security Context
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a Test Pod
&lt;/h3&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; - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Pod
metadata:
  name: security-test
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: test-container
    image: busybox:1.28
    command: ["sleep", "3600"]
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Security Settings
&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;# Check user and group&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;security-test &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;

&lt;span class="c"&gt;#output&lt;/span&gt;
&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000 &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000 &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2000,3000

&lt;span class="c"&gt;# Try to escalate privileges (should fail)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;security-test &lt;span class="nt"&gt;--&lt;/span&gt; su -

&lt;span class="c"&gt;#output&lt;/span&gt;
su: must be suid to work properly
&lt;span class="nb"&gt;command &lt;/span&gt;terminated with &lt;span class="nb"&gt;exit &lt;/span&gt;code 1

&lt;span class="c"&gt;# Check capabilities&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;security-test &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; /proc/1/status | &lt;span class="nb"&gt;grep &lt;/span&gt;Cap

&lt;span class="c"&gt;#output&lt;/span&gt;
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Security Context is a fundamental component of Kubernetes security that provides granular control over how pods and containers run in your cluster. By properly configuring Security Context, you can enhance your cluster's security posture and follow the principle of least privilege.&lt;/p&gt;

&lt;p&gt;Key takeaways: start with restrictive settings, test thoroughly, and combine Security Context with other Kubernetes security measures for comprehensive protection. Security is an ongoing process - regularly review and update your configurations as your applications and threat landscape evolve.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is based on Kubernetes official documentation and best practices for container security. Always refer to the latest Kubernetes documentation for the most up-to-date information.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" rel="noopener noreferrer"&gt;Kubernetes Official Documentation - Configure a Security Context for a Pod or Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#securitycontext-v1-core" rel="noopener noreferrer"&gt;Kubernetes API Reference - SecurityContext&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/" rel="noopener noreferrer"&gt;Pod Security Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/#linux-capabilities" rel="noopener noreferrer"&gt;Linux Capabilities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tutorials/security/seccomp/" rel="noopener noreferrer"&gt;Seccomp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tutorials/security/apparmor/" rel="noopener noreferrer"&gt;AppArmor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tutorials/security/selinux/" rel="noopener noreferrer"&gt;SELinux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>security</category>
      <category>cloudnative</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
