<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: iapilgrim</title>
    <description>The latest articles on Forem by iapilgrim (@pilgrim2go).</description>
    <link>https://forem.com/pilgrim2go</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%2F36351%2Fd3ac7864-d102-4f7a-abfa-c2ca03495434.png</url>
      <title>Forem: iapilgrim</title>
      <link>https://forem.com/pilgrim2go</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pilgrim2go"/>
    <language>en</language>
    <item>
      <title>Master the ECR Lifecycle: Automating Image Cleanup</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Mon, 23 Mar 2026 11:29:23 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/master-the-ecr-lifecycle-automating-image-cleanup-oh4</link>
      <guid>https://forem.com/pilgrim2go/master-the-ecr-lifecycle-automating-image-cleanup-oh4</guid>
      <description>&lt;h2&gt;
  
  
  Why Lifecycle Policies?
&lt;/h2&gt;

&lt;p&gt;Every time your CI/CD pipeline runs &lt;code&gt;docker push&lt;/code&gt;, you're adding roughly 200MB–1GB of data to your AWS bill. Without a policy, that data sits there forever. Lifecycle policies allow you to define rules like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Keep only the last 10 images."&lt;/li&gt;
&lt;li&gt;"Delete anything older than 14 days."&lt;/li&gt;
&lt;li&gt;"Expire untagged images immediately."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "Perfect" Policy for Dev/Staging
&lt;/h2&gt;

&lt;p&gt;For most teams, the best balance between "safety" and "savings" is a two-rule policy. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. The "Untagged" Rule (Priority 1)
&lt;/h3&gt;

&lt;p&gt;When you push a new image with the same tag (like &lt;code&gt;:latest&lt;/code&gt;), the old image becomes "untagged." These are orphaned layers that serve no purpose. Delete them after 24 hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The "Age" Rule (Priority 2)
&lt;/h3&gt;

&lt;p&gt;Delete any image that hasn't been pushed in the last 30 days. This ensures that even if you stop a project, its storage costs don't haunt you for years.&lt;/p&gt;

&lt;h3&gt;
  
  
  The JSON Configuration
&lt;/h3&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;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rulePriority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cleanup orphaned/untagged images"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"selection"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"tagStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"untagged"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sinceImagePushed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countUnit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expire"&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;span class="p"&gt;},&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;span class="nl"&gt;"rulePriority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Delete images older than 30 days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"selection"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"tagStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sinceImagePushed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countUnit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"countNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&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;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expire"&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;span class="p"&gt;}&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;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;h2&gt;
  
  
  Critical Concepts: &lt;code&gt;sinceImagePushed&lt;/code&gt; vs. &lt;code&gt;imageCountMoreThan&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sinceImagePushed&lt;/code&gt;&lt;/strong&gt;: Best for time-based compliance. (e.g., "We only keep 30 days of history").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;imageCountMoreThan&lt;/code&gt;&lt;/strong&gt;: Best for storage predictability. (e.g., "I only ever want to pay for 10 images per repo").&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Test Without Breaking Production
&lt;/h2&gt;

&lt;p&gt;The biggest fear is deleting an image that a production server might need during an auto-scaling event. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Lifecycle Policy Preview"&lt;/strong&gt; is your best friend. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your ECR Repo -&amp;gt; &lt;strong&gt;Lifecycle Policy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Actions&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Create Preview&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;AWS will generate a list of exactly which images &lt;em&gt;would&lt;/em&gt; be deleted if the policy ran right now. &lt;strong&gt;Check this list against your current production version before hitting save.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The 24-Hour Rule
&lt;/h2&gt;

&lt;p&gt;Remember: ECR policies are &lt;strong&gt;not instantaneous&lt;/strong&gt;. After you click "Save," AWS schedules a background task. It usually takes &lt;strong&gt;24 hours&lt;/strong&gt; for the images to actually disappear from your console and for the storage metrics to drop in CloudWatch.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pro-Tip: Infrastructure as Code (Terraform)
&lt;/h3&gt;

&lt;p&gt;Don't click around the console for 50 repos. Add this to your Terraform module:&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_ecr_lifecycle_policy"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecr_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;

  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&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;
{
    "rules": [
        {
            "rulePriority": 1,
            "description": "Keep last 30 images",
            "selection": {
                "tagStatus": "any",
                "countType": "imageCountMoreThan",
                "countNumber": 30
            },
            "action": {
                "type": "expire"
            }
        }
    ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>ecr</category>
      <category>cleanup</category>
    </item>
    <item>
      <title>Deploying a Base Sepolia Node with Docker</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Sat, 21 Mar 2026 07:22:07 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/deploying-a-base-sepolia-node-with-docker-4db4</link>
      <guid>https://forem.com/pilgrim2go/deploying-a-base-sepolia-node-with-docker-4db4</guid>
      <description>&lt;p&gt;We're using QuickNode for Base Sepolia. This post is how to setup our own node.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker &amp;amp; Docker Compose&lt;/strong&gt; installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L1 RPC Endpoint:&lt;/strong&gt; A synced Ethereum Sepolia node (e.g., Geth + Lighthouse).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L1 Beacon Endpoint:&lt;/strong&gt; Required for post-Canyon/Ecotone consensus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware:&lt;/strong&gt; Minimum 16GB RAM and 1.5TB+ NVMe SSD.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. The Automated Setup Script
&lt;/h3&gt;

&lt;p&gt;We use a wrapper script to handle directory creation, JWT generation, and repository patching. This ensures your local paths are correctly mapped into the Docker containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save as &lt;code&gt;setup-base-sepolia.sh&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Initializing Base Sepolia Setup..."&lt;/span&gt;

&lt;span class="nv"&gt;BASE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/node-data/testnet/base-sepolia"&lt;/span&gt;
&lt;span class="nv"&gt;REPO_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/base-node"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create directory layout&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📁 Creating data directories..."&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;op-geth,op-node,shared&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# 2. Generate the JWT Secret (The "Handshake" Key)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/shared/jwt.txt"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔑 Generating 32-byte hex JWT..."&lt;/span&gt;
  &lt;span class="c"&gt;# CRITICAL: No 0x prefix, no newlines.&lt;/span&gt;
  openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/shared/jwt.txt
  &lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASE_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/shared/jwt.txt
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 3. Clone the official Base Node Repo&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📦 Cloning base-org/node..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;git clone https://github.com/base-org/node.git &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# 4. Create .env.sepolia&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚙️ Writing environment configuration..."&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env.sepolia &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;
# L1 Endpoints (Replace with your actual L1 IPs)
OP_NODE_L1_ETH_RPC=http://testnet:8585
OP_NODE_L1_BEACON=http://testnet-lighthouse:5052

# L2 Execution &amp;amp; Auth
OP_NODE_L2_ENGINE_RPC=http://execution:8551
OP_NODE_L2_ENGINE_AUTH=jwt
OP_NODE_L2_ENGINE_JWT_SECRET=/shared/jwt.txt

# Network &amp;amp; Sync
OP_NODE_NETWORK=base-sepolia
OP_NODE_SYNC_MODE=snap
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# 5. Patch docker-compose.yml for custom volumes&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🛠 Patching docker-compose volumes..."&lt;/span&gt;
&lt;span class="c"&gt;# Map op-geth and op-node to host paths&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|${HOST_DATA_DIR}:/data|/node-data/testnet/base-sepolia/op-geth:/data|g'&lt;/span&gt; docker-compose.yml
&lt;span class="c"&gt;# Inject the shared JWT volume into both services&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/volumes:/a \      - /node-data/testnet/base-sepolia/shared:/shared'&lt;/span&gt; docker-compose.yml

&lt;span class="c"&gt;# 6. Start the stack&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting containers..."&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env.sepolia up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Critical Configuration Check
&lt;/h3&gt;

&lt;p&gt;For the node to function, your &lt;strong&gt;entrypoint scripts&lt;/strong&gt; inside the repo must use the correct environment variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;execution-entrypoint&lt;/code&gt;&lt;/strong&gt;: Ensure &lt;code&gt;--authrpc.jwtsecret="$OP_NODE_L2_ENGINE_JWT_SECRET"&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;op-node-entrypoint&lt;/code&gt;&lt;/strong&gt;: Ensure &lt;code&gt;--l2.jwt-secret="$OP_NODE_L2_ENGINE_JWT_SECRET"&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking&lt;/strong&gt;: Ensure &lt;code&gt;--rpc.addr=0.0.0.0&lt;/code&gt; is set in both to allow external monitoring.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Troubleshooting &amp;amp; Mastery 🔍
&lt;/h3&gt;

&lt;p&gt;If your node isn't syncing, check these three layers in order:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Layer 1: The JWT Handshake&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;If &lt;code&gt;op-node&lt;/code&gt; cannot talk to &lt;code&gt;op-geth&lt;/code&gt;, you will see &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;docker exec execution-1 cat /shared/jwt.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Ensure the file is exactly 64 characters long. If it contains "true" or a file path instead of hex, your script logic is overwriting the secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Layer 2: L1 Connectivity&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;op-node&lt;/code&gt; derives L2 blocks from L1 data. If L1 is down, L2 stops.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;docker exec node-1 curl -s http://testnet:8585&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Ensure your L1 node is fully synced and reachable on the Docker network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Layer 3: P2P Peering&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;snap&lt;/code&gt; sync mode, you need peers to download the state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;curl -s -d '{"id":1,"jsonrpc":"2.0","method":"opp2p_peerStats"}' http://localhost:9645 | jq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Ensure ports &lt;code&gt;30303&lt;/code&gt; (Geth) and &lt;code&gt;9222&lt;/code&gt; (Node) are open on your firewall.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. Monitoring Your Progress
&lt;/h3&gt;

&lt;p&gt;The most important command for a node operator is the &lt;strong&gt;Sync Status&lt;/strong&gt;. It tells you exactly where you are in history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Scoreboard" Command:&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;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="s1"&gt;'curl -s -X POST -H "Content-Type: application/json" --data "{\"jsonrpc\":\"2.0\",\"method\":\"optimism_syncStatus\",\"params\":[],\"id\":1}" http://localhost:9645 | jq ".result.local_safe_l2.number, .result.local_safe_l2.timestamp"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First Number:&lt;/strong&gt; Current L2 Block Height.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Second Number:&lt;/strong&gt; Unix Timestamp of that block (compare this to the current time to see how many days/months you are behind).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Generate&lt;/strong&gt; a clean JWT.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Align&lt;/strong&gt; your volumes so both containers see the same &lt;code&gt;/shared/jwt.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Point&lt;/strong&gt; to a healthy L1 RPC and Beacon node.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Monitor&lt;/strong&gt; via &lt;code&gt;optimism_syncStatus&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>base</category>
      <category>sepolia</category>
      <category>docker</category>
      <category>kvm</category>
    </item>
    <item>
      <title>Automating Container Image Updates with FluxCD (Hands-On Tutorial)</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Sat, 21 Mar 2026 05:06:50 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/automating-container-image-updates-with-fluxcd-hands-on-tutorial-88k</link>
      <guid>https://forem.com/pilgrim2go/automating-container-image-updates-with-fluxcd-hands-on-tutorial-88k</guid>
      <description>&lt;p&gt;Modern GitOps workflows aim to keep your Kubernetes cluster fully synchronized with what is defined in Git. One powerful feature of Flux is &lt;strong&gt;Image Automation&lt;/strong&gt;, which automatically updates container image tags in your Git repository whenever a new image becomes available.&lt;/p&gt;

&lt;p&gt;In this tutorial, we walk through how image automation works and how to troubleshoot a common issue involving Git authentication.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What is Flux Image Automation?
&lt;/h2&gt;

&lt;p&gt;FluxCD provides several components that work together to automate image updates.&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&lt;/p&gt;

&lt;p&gt;Container Registry&lt;br&gt;
↓&lt;br&gt;
ImageRepository (scans tags)&lt;br&gt;
↓&lt;br&gt;
ImagePolicy (selects allowed tags)&lt;br&gt;
↓&lt;br&gt;
ImageUpdateAutomation (commits updates to Git)&lt;br&gt;
↓&lt;br&gt;
Flux Kustomization deploys updated workload&lt;/p&gt;

&lt;p&gt;Instead of manually updating image tags in Git, Flux automatically commits the new tag when a matching image appears in your container registry.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Repository Structure Used in This Lab
&lt;/h2&gt;

&lt;p&gt;Example GitOps layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux-minikube-lab
├── apps
│   └── web-server
│       ├── web-server.yaml
│       ├── sealed-db-pass.yaml
│       └── kustomization.yaml
│
└── clusters
    └── my-cluster
        ├── image-reflect.yaml
        ├── image-policy.yaml
        ├── web-server-sync.yaml
        └── kustomization.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cluster Kustomization references infrastructure and application sync resources.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Application Deployment
&lt;/h2&gt;

&lt;p&gt;The web server manifests are defined in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/web-server/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;kustomization.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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web-server.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sealed-db-pass.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Flux Kustomization sync object deploys the app:&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;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kustomization&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;web-server-sync&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flux-system&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./apps/web-server&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;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepository&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;flux-system&lt;/span&gt;
  &lt;span class="na"&gt;targetNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs Flux to deploy the web server into the &lt;code&gt;engineering&lt;/code&gt; namespace.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Image Automation Setup
&lt;/h2&gt;

&lt;p&gt;Three resources enable automated updates:&lt;/p&gt;

&lt;h3&gt;
  
  
  Image Repository
&lt;/h3&gt;

&lt;p&gt;This scans container registry tags.&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;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImageRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Policy
&lt;/h3&gt;

&lt;p&gt;Defines which tags are acceptable (e.g., semver or latest).&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;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImagePolicy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Update Automation
&lt;/h3&gt;

&lt;p&gt;Updates the Git repo when a new tag matches the policy.&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;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImageUpdateAutomation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Triggering Automation
&lt;/h2&gt;

&lt;p&gt;To manually trigger automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux reconcile image update flux-system
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flux will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check registry tags&lt;/li&gt;
&lt;li&gt;Apply the image policy&lt;/li&gt;
&lt;li&gt;Update manifests in Git&lt;/li&gt;
&lt;li&gt;Commit and push the change&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  6. Troubleshooting a Common Error
&lt;/h2&gt;

&lt;p&gt;While testing automation, the following error appeared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failed to update source:
failed to push to remote:
ERROR: The key you are authenticating with has been marked as read only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the automation status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux get image update
&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 plaintext"&gt;&lt;code&gt;READY: False
MESSAGE: failed to push to remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Root Cause
&lt;/h2&gt;

&lt;p&gt;ImageUpdateAutomation needs &lt;strong&gt;write access to the Git repository&lt;/strong&gt; to commit updated image tags.&lt;/p&gt;

&lt;p&gt;However, the repository authentication key was configured as &lt;strong&gt;read-only&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This allowed Flux to pull manifests but prevented it from pushing updates.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Fix: Recreate the Flux Git Secret with Write Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Generate a New SSH Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ssh-keygen
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2 — Add the Public Key to GitHub
&lt;/h3&gt;

&lt;p&gt;Navigate to the repository settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Deploy Keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the new public key and enable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allow write access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3 — Recreate the Flux Git Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux create secret git flux-system \
  --url=ssh://git@github.com/pilgrim2go/flux-minikube-lab \
&lt;/span&gt;&lt;span class="gp"&gt;  --private-key-file=$&lt;/span&gt;PWD/fluxcd-test &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -n flux-system
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates the authentication credentials used by Flux.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4 — Re-run Automation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux reconcile image update flux-system
&lt;/span&gt;&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 console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux get image update
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;READY: True
MESSAGE: committed and pushed update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new commit should appear in the repository with the updated image tag.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Helpful Debug Commands
&lt;/h2&gt;

&lt;p&gt;Check Flux resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux get all
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect automation status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux get image update
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View controller logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;kubectl logs -n flux-system deploy/image-automation-controller
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trigger Git sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;flux reconcile source git flux-system
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Best Practices
&lt;/h2&gt;

&lt;p&gt;When using Flux image automation:&lt;/p&gt;

&lt;p&gt;• Use a dedicated deploy key or bot account&lt;br&gt;
• Ensure Git credentials allow &lt;strong&gt;read and write access&lt;/strong&gt;&lt;br&gt;
• Keep Git as the &lt;strong&gt;single source of truth&lt;/strong&gt;&lt;br&gt;
• Avoid manual cluster changes outside GitOps&lt;/p&gt;




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

&lt;p&gt;Flux image automation eliminates manual image updates and ensures that your Kubernetes workloads always run the latest approved container images.&lt;/p&gt;

&lt;p&gt;With the proper Git authentication setup, Flux can automatically:&lt;/p&gt;

&lt;p&gt;• Detect new container images&lt;br&gt;
• Update manifests in Git&lt;br&gt;
• Trigger GitOps deployments&lt;/p&gt;

&lt;p&gt;This enables a fully automated and auditable Kubernetes delivery pipeline.&lt;/p&gt;

</description>
      <category>fluxcd</category>
      <category>cicd</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>FluxCD Image Automation Error Troubleshooting</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Sat, 21 Mar 2026 05:05:02 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/fluxcd-image-automation-error-troubleshooting-1el2</link>
      <guid>https://forem.com/pilgrim2go/fluxcd-image-automation-error-troubleshooting-1el2</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux reconcile image update flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failed to update source: failed to push to remote
ERROR: The key you are authenticating with has been marked as read only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get image update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;READY: False
MESSAGE: failed to push to remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Root Cause
&lt;/h2&gt;

&lt;p&gt;FluxCD &lt;strong&gt;ImageUpdateAutomation&lt;/strong&gt; needs to &lt;strong&gt;commit and push updates to the Git repository&lt;/strong&gt; when it updates container image tags.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Container Registry
        ↓
ImageRepository
        ↓
ImagePolicy
        ↓
ImageUpdateAutomation
        ↓
Git Commit + Push
        ↓
Flux Kustomization deploys update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Git credential is &lt;strong&gt;read-only&lt;/strong&gt;, the push fails.&lt;/p&gt;




&lt;h2&gt;
  
  
  Diagnosis Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Check Image Automation Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get image update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;READY: False
failed to push to remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Inspect the Automation Object
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get imageupdateautomation &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STATUS: failed to update source
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Check the Git Source
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get sources git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms Flux can &lt;strong&gt;read&lt;/strong&gt; the repo.&lt;/p&gt;

&lt;p&gt;But pushing still fails if the key is read-only.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Confirm Git Authentication
&lt;/h3&gt;

&lt;p&gt;Check the Git secret:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This secret contains the SSH key Flux uses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fix Implemented
&lt;/h2&gt;

&lt;p&gt;You recreated the Git authentication secret with a &lt;strong&gt;write-enabled SSH key&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Generate SSH Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2️⃣ Add Public Key to GitHub
&lt;/h3&gt;

&lt;p&gt;Go to repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Deploy Keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fluxcd-test.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Allow write access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3️⃣ Recreate Flux Git Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux create secret git flux-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssh://git@github.com/pilgrim2go/flux-minikube-lab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--private-key-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;/fluxcd-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This updates the Git credential used by Flux.&lt;/p&gt;




&lt;h3&gt;
  
  
  4️⃣ Trigger Automation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux reconcile image update flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Expected Result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get image update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;READY: True
MESSAGE: committed and pushed update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should also see a commit in Git like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux: update image tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Useful Debug Commands
&lt;/h2&gt;

&lt;p&gt;Check full Flux status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux get all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check automation logs:&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;-n&lt;/span&gt; flux-system deploy/image-automation-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test Git sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux reconcile &lt;span class="nb"&gt;source &lt;/span&gt;git flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practice
&lt;/h2&gt;

&lt;p&gt;Use a &lt;strong&gt;dedicated Flux deploy key&lt;/strong&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read + write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of personal access tokens when using SSH Git repositories.&lt;/p&gt;

</description>
      <category>fluxcd</category>
      <category>minikube</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>FluxCD journey with Minikube</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Fri, 20 Mar 2026 14:33:52 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/fluxcd-journey-with-minikube-16pg</link>
      <guid>https://forem.com/pilgrim2go/fluxcd-journey-with-minikube-16pg</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Phase 1: The Manual Foundation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Set up the cluster and deploy a "Hello World" app the old-fashioned way to understand what we are automating.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Step 1: Install Tools
&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;# Install the Big Three (macOS example)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;minikube kubectl fluxcd/tap/flux

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🏗️ Step 2: Start Minikube
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube start &lt;span class="nt"&gt;--cpus&lt;/span&gt; 2 &lt;span class="nt"&gt;--memory&lt;/span&gt; 4096 &lt;span class="nt"&gt;--driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;ingress

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📂 Step 3: Directory Layout
&lt;/h3&gt;

&lt;p&gt;Create this structure on your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux-lab/
└── base/
    ├── kustomization.yaml
    └── web-server.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📄 Step 4: The Manifests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;flux-lab/base/web-server.yaml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-server&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;engineering&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;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;p&gt;&lt;strong&gt;&lt;code&gt;flux-lab/base/kustomization.yaml&lt;/code&gt;&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web-server.yaml&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Step 5: Deploy Manually
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace engineering
kubectl apply &lt;span class="nt"&gt;-k&lt;/span&gt; flux-lab/base/
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; engineering

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🤖 Phase 2: The Great Automation (FluxCD)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect GitHub to Minikube. From this point on, we never use &lt;code&gt;kubectl apply&lt;/code&gt; again.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Step 1: Environment Setup
&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;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_personal_access_token
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_github_username

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🏗️ Step 2: Bootstrap Flux
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flux bootstrap github &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_USER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flux-minikube-lab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clusters/my-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--personal&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📂 Step 3: Final Git Directory Layout
&lt;/h3&gt;

&lt;p&gt;Clone your new repo and organize it exactly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux-minikube-lab/
├── apps/
│   └── web-server/
│       ├── kustomization.yaml
│       └── web-server.yaml
└── clusters/
    └── my-cluster/
        ├── flux-system/         # (Auto-generated)
        └── web-server-sync.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📄 Step 4: Create the "Sync" Instruction
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;clusters/my-cluster/web-server-sync.yaml&lt;/code&gt;&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;kustomize.toolkit.fluxcd.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;Kustomization&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;web-server-sync&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flux-system&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./apps/web-server&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;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepository&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;flux-system&lt;/span&gt;
  &lt;span class="na"&gt;targetNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;engineering&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Step 5: Push and Pray (The GitOps Way)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Onboard web-server to GitOps"&lt;/span&gt;
git push origin main

&lt;span class="c"&gt;# Force immediate sync&lt;/span&gt;
flux reconcile kustomization flux-system &lt;span class="nt"&gt;--with-source&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔐 Phase 3: The Secret Sauce (Sealed Secrets)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Store passwords in GitHub securely using encryption.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏗️ Step 1: Install Infrastructure
&lt;/h3&gt;

&lt;p&gt;Place these files in &lt;code&gt;infrastructure/sources/&lt;/code&gt; and &lt;code&gt;infrastructure/controllers/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;clusters/my-cluster/infra-sync.yaml&lt;/code&gt;&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;kustomize.toolkit.fluxcd.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;Kustomization&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;infra-sync&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flux-system&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1h&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;./infrastructure&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;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepository&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;flux-system&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📂 Step 2: Final Phase 3 Directory Layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux-minikube-lab/
├── apps/
│   └── web-server/
│       ├── kustomization.yaml   # (Update to include sealed-db-pass.yaml)
│       ├── web-server.yaml
│       └── sealed-db-pass.yaml  # (Generated)
├── clusters/
│   └── my-cluster/
│       ├── infra-sync.yaml
│       └── web-server-sync.yaml
└── infrastructure/
    ├── controllers/
    │   └── sealed-secrets.yaml
    └── sources/
        └── sealed-secrets.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔐 Step 3: Create an Encrypted Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create a raw secret (DO NOT PUSH TO GIT)&lt;/span&gt;
kubectl create secret generic mwd-db-pass &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SuperSecret123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; engineering &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; temp.yaml

&lt;span class="c"&gt;# 2. Encrypt it using the cluster's key&lt;/span&gt;
kubeseal &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--controller-name&lt;/span&gt; sealed-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--controller-namespace&lt;/span&gt; flux-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; yaml &amp;lt; temp.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; apps/web-server/sealed-db-pass.yaml

&lt;span class="c"&gt;# 3. Clean up&lt;/span&gt;
&lt;span class="nb"&gt;rm &lt;/span&gt;temp.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚀 Step 4: Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add sealed secret"&lt;/span&gt;
git push origin main
flux reconcile kustomization infra-sync &lt;span class="nt"&gt;--with-source&lt;/span&gt;
flux reconcile kustomization web-server-sync &lt;span class="nt"&gt;--with-source&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧠 Summary of Progress
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1:&lt;/strong&gt; Learned &lt;strong&gt;Kubernetes&lt;/strong&gt; resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2:&lt;/strong&gt; Learned &lt;strong&gt;FluxCD&lt;/strong&gt; automation and the "Pull Model."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3:&lt;/strong&gt; Learned &lt;strong&gt;Security&lt;/strong&gt; and encryption in Git.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>fluxcd</category>
      <category>minikube</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>🧠 Migrating BSC Testnet Data to a New Disk (KVM + Docker)</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Thu, 19 Mar 2026 03:44:50 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/migrating-bsc-testnet-data-to-a-new-disk-kvm-docker-4gf9</link>
      <guid>https://forem.com/pilgrim2go/migrating-bsc-testnet-data-to-a-new-disk-kvm-docker-4gf9</guid>
      <description>&lt;p&gt;Running blockchain nodes at scale quickly turns into a &lt;strong&gt;storage management problem&lt;/strong&gt;.&lt;br&gt;
In this guide, I’ll walk through a real-world migration of a &lt;strong&gt;BSC testnet dataset (~243GB)&lt;/strong&gt; to a new disk — without breaking the node — and share key lessons learned.&lt;/p&gt;
&lt;h2&gt;
  
  
  📊 Initial Situation
&lt;/h2&gt;

&lt;p&gt;Inside the VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/dev/vdb1 → /node-data (6.3T disk, almost full)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mainnet: ~5.8TB&lt;/li&gt;
&lt;li&gt;Testnet: ~243GB&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disk usage: 100%
Free space: ~41GB ⚠️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 Risk: node crash, DB corruption, sync failure&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Goal
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Move &lt;strong&gt;testnet data&lt;/strong&gt; to a new disk&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;mainnet untouched&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Avoid complex Docker changes&lt;/li&gt;
&lt;li&gt;Minimize downtime&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture Before
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vdb (6.3T)
└── /node-data
    ├── mainnet (~5.8T)
    └── testnet (~243G)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Architecture After
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vdb → mainnet
vdc → testnet

/bsc-testnet → real mount
/node-data/testnet → bind mount → /bsc-testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker still uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/node-data/testnet:/bsc/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 No container config change needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Step-by-Step Migration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Attach new disk (on host)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qemu-img create &lt;span class="nt"&gt;-f&lt;/span&gt; qcow2 /path/bsc-testnet.qcow2 400G

virsh attach-disk bsc &lt;span class="se"&gt;\&lt;/span&gt;
  /path/bsc-testnet.qcow2 &lt;span class="se"&gt;\&lt;/span&gt;
  vdc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--targetbus&lt;/span&gt; virtio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subdriver&lt;/span&gt; qcow2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--persistent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2️⃣ Prepare disk (inside VM)
&lt;/h3&gt;

&lt;p&gt;Skip partitioning (simpler):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkfs.ext4 /dev/vdc
&lt;span class="nb"&gt;mkdir&lt;/span&gt; /bsc-testnet
mount /dev/vdc /bsc-testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3️⃣ Copy data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avh&lt;/span&gt; /node-data/testnet/ /bsc-testnet/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4️⃣ Switch using bind mount (no Docker change)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop testnet

&lt;span class="nb"&gt;mv&lt;/span&gt; /node-data/testnet /node-data/testnet-old
&lt;span class="nb"&gt;mkdir&lt;/span&gt; /node-data/testnet

mount &lt;span class="nt"&gt;--bind&lt;/span&gt; /bsc-testnet /node-data/testnet

docker start testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5️⃣ Cleanup (after verification)
&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /node-data/testnet-old
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6️⃣ Make persistent
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;vdc-uuid&amp;gt; /bsc-testnet ext4 defaults 0 0
/bsc-testnet /node-data/testnet none &lt;span class="nb"&gt;bind &lt;/span&gt;0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;






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

&lt;h3&gt;
  
  
  1️⃣ You don’t need to touch Docker
&lt;/h3&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;editing volumes&lt;/li&gt;
&lt;li&gt;recreating containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Just move the &lt;strong&gt;filesystem underneath&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is safer and faster.&lt;/p&gt;




&lt;h3&gt;
  
  
  2️⃣ Bind mounts are extremely powerful
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/node-data/testnet → /bsc-testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Acts like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transparent redirect&lt;/li&gt;
&lt;li&gt;zero config change&lt;/li&gt;
&lt;li&gt;instant rollback&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3️⃣ “target is busy” = you are inside the directory
&lt;/h3&gt;

&lt;p&gt;Classic mistake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;umount /node-data/testnet
→ target is busy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cwd &lt;span class="o"&gt;=&lt;/span&gt; /node-data/testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;/div&gt;






&lt;h3&gt;
  
  
  4️⃣ Duplicate mounts can happen easily
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;mount --bind&lt;/code&gt; multiple times creates &lt;strong&gt;stacked mounts&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount | &lt;span class="nb"&gt;grep &lt;/span&gt;testnet
findmnt /node-data/testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;umount /node-data/testnet &lt;span class="o"&gt;(&lt;/span&gt;repeat &lt;span class="k"&gt;until &lt;/span&gt;gone&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5️⃣ Never unmount while container is running
&lt;/h3&gt;

&lt;p&gt;Even if it “works”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DB writes can fail&lt;/li&gt;
&lt;li&gt;corruption risk&lt;/li&gt;
&lt;li&gt;node may resync from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Always:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop → umount → remount → start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6️⃣ Partitioning is optional
&lt;/h3&gt;

&lt;p&gt;For dedicated disks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkfs.ext4 /dev/vdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is simpler than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fdisk → /dev/vdc1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7️⃣ Blockchain nodes will ALWAYS outgrow your disk
&lt;/h3&gt;

&lt;p&gt;After migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mainnet still ~92% full
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 This is temporary relief.&lt;/p&gt;

&lt;p&gt;You must plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disk expansion (&lt;code&gt;qemu-img resize&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;or data rebalancing&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📈 Results
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;Freed ~243GB on main disk&lt;/li&gt;
&lt;li&gt;Isolated IO between mainnet/testnet&lt;/li&gt;
&lt;li&gt;No Docker reconfiguration&lt;/li&gt;
&lt;li&gt;Minimal downtime (~1–2 minutes)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Final Takeaway
&lt;/h2&gt;

&lt;p&gt;The key mindset shift:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don’t move applications — move the filesystem underneath them.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;ul&gt;
&lt;li&gt;reduces risk&lt;/li&gt;
&lt;li&gt;simplifies operations&lt;/li&gt;
&lt;li&gt;scales better for large datasets (TBs)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 What’s Next
&lt;/h2&gt;

&lt;p&gt;For production-grade setups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate disks per chain&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;virsh blockcopy&lt;/code&gt; for live migration&lt;/li&gt;
&lt;li&gt;Implement storage balancing strategy across NVMe pools&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bsc</category>
      <category>kvm</category>
      <category>blockchain</category>
      <category>docker</category>
    </item>
    <item>
      <title>vLLM Request Lifecycle (Where TTFT is measured)</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Wed, 11 Mar 2026 13:12:54 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/vllm-request-lifecycle-where-ttft-is-measured-5cca</link>
      <guid>https://forem.com/pilgrim2go/vllm-request-lifecycle-where-ttft-is-measured-5cca</guid>
      <description>&lt;h2&gt;
  
  
  Observing LLM Latency: Monitoring Time-To-First-Token in vLLM
&lt;/h2&gt;

&lt;p&gt;Large language model APIs often feel fast even when generating long responses.&lt;br&gt;
The key reason is &lt;strong&gt;streaming tokens&lt;/strong&gt; — the response starts quickly while the rest of the answer is still being generated.&lt;/p&gt;

&lt;p&gt;The most important metric for this experience is &lt;strong&gt;Time-To-First-Token (TTFT)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This tutorial explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what TTFT is&lt;/li&gt;
&lt;li&gt;how &lt;strong&gt;vLLM exposes TTFT metrics&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;how &lt;strong&gt;Prometheus calculates percentiles&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;how to build a &lt;strong&gt;Grafana dashboard&lt;/strong&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%2Fm435scmyo0g3vsfiif5m.png" alt="request lifecycle" width="598" height="880"&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What is Time-To-First-Token (TTFT)?
&lt;/h2&gt;

&lt;p&gt;TTFT measures how long it takes from &lt;strong&gt;request arrival to the first generated token&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;LLM inference pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request arrives
      ↓
Prompt tokenization
      ↓
Prefill (model processes prompt)
      ↓
FIRST TOKEN GENERATED  ← TTFT measured here
      ↓
Token streaming
      ↓
Response complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users perceive the system as &lt;strong&gt;fast&lt;/strong&gt; if TTFT is small.&lt;/p&gt;

&lt;p&gt;Typical targets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Good TTFT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat UI&lt;/td&gt;
&lt;td&gt;&amp;lt;2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU inference&lt;/td&gt;
&lt;td&gt;&amp;lt;4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy batching&lt;/td&gt;
&lt;td&gt;&amp;lt;8s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  vLLM TTFT Metric
&lt;/h2&gt;

&lt;p&gt;vLLM exports a &lt;strong&gt;Prometheus histogram metric&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight prometheus"&gt;&lt;code&gt;&lt;span class="n"&gt;vllm:time_to_first_token_seconds_bucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight prometheus"&gt;&lt;code&gt;&lt;span class="n"&gt;vllm:time_to_first_token_seconds_bucket&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;vllm:time_to_first_token_seconds_bucket&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2.5"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;73&lt;/span&gt;
&lt;span class="n"&gt;vllm:time_to_first_token_seconds_bucket&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;103&lt;/span&gt;
&lt;span class="n"&gt;vllm:time_to_first_token_seconds_bucket&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;104&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These represent &lt;strong&gt;histogram buckets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example interpretation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TTFT&lt;/th&gt;
&lt;th&gt;Requests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;≤1s&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;≤2.5s&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;≤10s&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;≤20s&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Buckets are &lt;strong&gt;cumulative counters&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Causes of High TTFT
&lt;/h2&gt;

&lt;p&gt;If TTFT increases, check:&lt;/p&gt;

&lt;h3&gt;
  
  
  GPU saturation
&lt;/h3&gt;

&lt;p&gt;Too many requests in queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large prompts
&lt;/h3&gt;

&lt;p&gt;Prefill phase takes longer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batch scheduling delay
&lt;/h3&gt;

&lt;p&gt;Large batch sizes increase wait time.&lt;/p&gt;

&lt;h3&gt;
  
  
  KV cache limits
&lt;/h3&gt;

&lt;p&gt;Cache misses slow inference.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended vLLM Observability Metrics
&lt;/h2&gt;

&lt;p&gt;Monitor these together:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFT&lt;/td&gt;
&lt;td&gt;time until response starts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tokens/sec&lt;/td&gt;
&lt;td&gt;generation speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;request latency&lt;/td&gt;
&lt;td&gt;full response time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;active requests&lt;/td&gt;
&lt;td&gt;queue pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU utilization&lt;/td&gt;
&lt;td&gt;hardware bottleneck&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Monitoring TTFT helps maintain a responsive LLM system.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vLLM metrics
     ↓
Prometheus histogram
     ↓
PromQL percentile query
     ↓
Grafana visualization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With proper monitoring, you can detect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU saturation&lt;/li&gt;
&lt;li&gt;batching issues&lt;/li&gt;
&lt;li&gt;prompt bottlenecks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;before users notice degraded performance.&lt;/p&gt;

</description>
      <category>vllm</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Troubleshooting: Fixing k6 GPG Key Errors in Ubuntu</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Wed, 11 Mar 2026 03:41:07 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/troubleshooting-fixing-k6-gpg-key-errors-in-ubuntu-44hg</link>
      <guid>https://forem.com/pilgrim2go/troubleshooting-fixing-k6-gpg-key-errors-in-ubuntu-44hg</guid>
      <description>&lt;p&gt;Setting up k6 on Ubuntu should be a straightforward process, but GPG key errors can occasionally turn a "five-minute task" into a troubleshooting marathon. If you’ve encountered the dreaded &lt;code&gt;NO_PUBKEY&lt;/code&gt; or &lt;code&gt;unsupported filetype&lt;/code&gt; errors, this guide will walk you through the fix and the proper installation.&lt;/p&gt;

&lt;p&gt;Modern Linux distributions have moved away from &lt;code&gt;apt-key add&lt;/code&gt; (which is now deprecated) toward specific keyrings located in &lt;code&gt;/usr/share/keyrings&lt;/code&gt;. This "sandboxes" the security keys so that the k6 key can only be used to verify k6 packages, making your overall system much more secure.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Troubleshooting: Fixing k6 GPG Key Errors
&lt;/h2&gt;

&lt;p&gt;When you see errors like &lt;strong&gt;"The following signatures couldn't be verified"&lt;/strong&gt; or &lt;strong&gt;"unsupported filetype,"&lt;/strong&gt; it means your system doesn't trust the k6 repository or the key file you downloaded is corrupted (usually saved as plain text instead of binary).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Clean Up Old Keys
&lt;/h3&gt;

&lt;p&gt;If you have a broken keyring file, &lt;code&gt;apt&lt;/code&gt; will continue to complain until it's gone. Start by removing any existing k6 keyring:&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;sudo rm&lt;/span&gt; /usr/share/keyrings/k6-archive-keyring.gpg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Download and "Dearmor" the Key
&lt;/h3&gt;

&lt;p&gt;The key must be in a binary format for modern Ubuntu versions. We use &lt;code&gt;gpg --dearmor&lt;/code&gt; to convert the text-based key from k6 into the required binary format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://dl.k6.io/key.gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/k6-archive-keyring.gpg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Set Correct Permissions
&lt;/h3&gt;

&lt;p&gt;System services need to be able to read this file. Set the permissions to &lt;strong&gt;644&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /usr/share/keyrings/k6-archive-keyring.gpg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 Setting Up the Repository
&lt;/h2&gt;

&lt;p&gt;Now that the security "handshake" is ready, you need to tell Ubuntu exactly where to find k6 and which key to use for verification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add the k6 Source List
&lt;/h3&gt;

&lt;p&gt;Run this command to create (or overwrite) the source file. Note the &lt;code&gt;signed-by&lt;/code&gt; tag—this is the secret sauce that links the repo to the key we just 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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/k6.list

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Final Installation
&lt;/h2&gt;

&lt;p&gt;With the repository correctly configured and the key verified, you can now update your package list and install k6.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Update and Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;k6

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Verify Success
&lt;/h3&gt;

&lt;p&gt;Confirm k6 is installed and ready to go by checking the version:&lt;br&gt;
&lt;/p&gt;

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

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>k6</category>
      <category>locust</category>
      <category>hey</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Overlay vs Underlay Networking Across Regions with Azure Kubernetes Service</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Mon, 09 Mar 2026 03:17:28 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/overlay-vs-underlay-networking-across-regions-with-azure-kubernetes-service-266o</link>
      <guid>https://forem.com/pilgrim2go/overlay-vs-underlay-networking-across-regions-with-azure-kubernetes-service-266o</guid>
      <description>&lt;p&gt;In this tutorial we build a &lt;strong&gt;complete AKS networking lab&lt;/strong&gt; to demonstrate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AKS &lt;strong&gt;Overlay networking&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AKS &lt;strong&gt;Underlay (Azure CNI) networking&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-region VNet peering&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pod-to-pod communication across clusters&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access to Azure SQL via Private Endpoint&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service exposure via NodePort&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final architecture:&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%2Ftxt30ld0j5kw793hunh5.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%2Ftxt30ld0j5kw793hunh5.png" alt="Network Design" width="683" height="481"&gt;&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;Region 1 (Southeast Asia)
VNet: 10.0.0.0/16
--------------------------------

AKS Overlay Cluster
Nodes: 10.0.x.x
Pods : 192.168.x.x

Private Endpoint
Azure SQL
10.0.4.4

Private DNS Zone
privatelink.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Region 2 (East Asia)
VNet: 10.1.0.0/16
--------------------------------

AKS Underlay Cluster
Nodes: 10.1.x.x
Pods : 10.1.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VNet Peering
10.0.0.0/16  &amp;lt;----&amp;gt;  10.1.0.0/16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1. Create the Overlay AKS Cluster
&lt;/h2&gt;

&lt;p&gt;Overlay networking allows pods to use &lt;strong&gt;non-VNet IP ranges&lt;/strong&gt;, which prevents subnet exhaustion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create resource group
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RG_NETWORK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg-aks-network
&lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;southeastasia

az group create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create VNet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VNET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vnet-aks-overlay

az network vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-name&lt;/span&gt; aks-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-prefix&lt;/span&gt; 10.0.1.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create AKS overlay cluster
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AKS_OVERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aks-overlay

&lt;span class="nv"&gt;SUBNET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az network vnet subnet show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aks-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

az aks create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$AKS_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-count&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-plugin&lt;/span&gt; azure &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-plugin-mode&lt;/span&gt; overlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pod-cidr&lt;/span&gt; 192.168.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-subnet-id&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--generate-ssh-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Connect kubectl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks get-credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$AKS_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--context&lt;/span&gt; aks-overlay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Verify overlay pod networking
&lt;/h3&gt;

&lt;p&gt;Deploy a test 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 run overlay-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;busybox &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Never &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check pod IP:&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;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;overlay-test   192.168.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overlay pods use &lt;strong&gt;192.168.x.x&lt;/strong&gt;, not VNet IPs.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Create Azure SQL with Private Endpoint
&lt;/h2&gt;

&lt;p&gt;Create SQL server:&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;SQL_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aks-lab-sql-31445

az sql server create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SQL_SERVER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--admin-user&lt;/span&gt; sqladmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--admin-password&lt;/span&gt; &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create private endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-endpoint create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sql-pe &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt; aks-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--private-connection-resource-id&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;az sql server show &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SQL_SERVER&lt;/span&gt; &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-id&lt;/span&gt; sqlServer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--connection-name&lt;/span&gt; sql-pe-connection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create Private DNS zone
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-dns zone create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; privatelink.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Link overlay VNet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-dns &lt;span class="nb"&gt;link &lt;/span&gt;vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone-name&lt;/span&gt; privatelink.database.windows.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; link-overlay-vnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--virtual-network&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--registration-enabled&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Test SQL access from overlay pod
&lt;/h3&gt;



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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nslookup &amp;lt;sql-server&amp;gt;.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.0.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Create Underlay AKS Cluster in Another Region
&lt;/h2&gt;

&lt;p&gt;Underlay clusters assign &lt;strong&gt;real VNet IPs to pods&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Create resource group
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RG_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg-aks-underlay
&lt;span class="nv"&gt;UNDERLAY_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eastasia

az group create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$UNDERLAY_LOCATION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create VNet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VNET_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vnet-aks-underlay

az network vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$UNDERLAY_LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.1.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-name&lt;/span&gt; aks-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-prefix&lt;/span&gt; 10.1.1.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create underlay AKS cluster
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SUBNET_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az network vnet subnet show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aks-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

az aks create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aks-underlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$UNDERLAY_LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-count&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-plugin&lt;/span&gt; azure &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-subnet-id&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-cidr&lt;/span&gt; 172.17.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dns-service-ip&lt;/span&gt; 172.17.0.10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--generate-ssh-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Connect kubectl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks get-credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aks-underlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--context&lt;/span&gt; aks-underlay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Verify pod IP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;--context&lt;/span&gt; aks-underlay run underlay-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;busybox &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Never &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.1.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pods now consume &lt;strong&gt;VNet IPs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Peer the VNets
&lt;/h2&gt;

&lt;p&gt;Overlay and underlay clusters must communicate.&lt;/p&gt;




&lt;h3&gt;
  
  
  Overlay → Underlay
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet peering create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; overlay-to-underlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--remote-vnet&lt;/span&gt; /subscriptions/&amp;lt;sub&amp;gt;/resourceGroups/&lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt;/providers/Microsoft.Network/virtualNetworks/&lt;span class="nv"&gt;$VNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-vnet-access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Underlay → Overlay
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet peering create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; underlay-to-overlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--remote-vnet&lt;/span&gt; /subscriptions/&amp;lt;sub&amp;gt;/resourceGroups/&lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt;/providers/Microsoft.Network/virtualNetworks/&lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-vnet-access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Link Private DNS to Underlay VNet
&lt;/h2&gt;

&lt;p&gt;Without this step, the underlay cluster resolves &lt;strong&gt;public SQL endpoints&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;az network private-dns &lt;span class="nb"&gt;link &lt;/span&gt;vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone-name&lt;/span&gt; privatelink.database.windows.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; link-underlay-vnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--virtual-network&lt;/span&gt; /subscriptions/&amp;lt;sub&amp;gt;/resourceGroups/&lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt;/providers/Microsoft.Network/virtualNetworks/&lt;span class="nv"&gt;$VNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--registration-enabled&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Communication Testing Scenarios
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Scenario 1 — Overlay Pod → Underlay Pod
&lt;/h2&gt;

&lt;p&gt;Get underlay pod IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.1.1.18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From overlay 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 &lt;span class="nt"&gt;--context&lt;/span&gt; aks-overlay &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; overlay-test &lt;span class="nt"&gt;--&lt;/span&gt; sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ping 10.1.1.18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success proves &lt;strong&gt;cross-region VNet routing works&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scenario 2 — Underlay Pod → Azure SQL Private Endpoint
&lt;/h2&gt;

&lt;p&gt;Enter underlay 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 &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; underlay-test &lt;span class="nt"&gt;--&lt;/span&gt; sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nslookup &amp;lt;sql-server&amp;gt;.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.0.4.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test SQL port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-zv&lt;/span&gt; &amp;lt;sql-server&amp;gt;.database.windows.net 1433
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connection succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Scenario 3 — Overlay Pod → Underlay Service
&lt;/h2&gt;

&lt;p&gt;Deploy nginx in underlay cluster:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Expose via NodePort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl expose deployment nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; NodePort &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; nginx-nodeport
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;80:31904/TCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get node IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.1.1.33
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test from overlay 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 &lt;span class="nt"&gt;--context&lt;/span&gt; aks-overlay &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; overlay-test &lt;span class="nt"&gt;--&lt;/span&gt; sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wget -qO- http://10.1.1.33:31904
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome to nginx!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Networking Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Overlay&lt;/th&gt;
&lt;th&gt;Underlay&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pod IP&lt;/td&gt;
&lt;td&gt;Overlay CIDR&lt;/td&gt;
&lt;td&gt;VNet subnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VNet IP usage&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;Subnet exhaustion risk&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct VNet reachability&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;p&gt;This lab demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overlay networking&lt;/li&gt;
&lt;li&gt;Underlay networking&lt;/li&gt;
&lt;li&gt;Cross-region VNet peering&lt;/li&gt;
&lt;li&gt;AKS pod communication&lt;/li&gt;
&lt;li&gt;Azure SQL Private Endpoint connectivity&lt;/li&gt;
&lt;li&gt;Kubernetes service exposure&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%2Ftxt30ld0j5kw793hunh5.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%2Ftxt30ld0j5kw793hunh5.png" alt="Network Design" width="683" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aks</category>
      <category>azure</category>
      <category>networking</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Deploy Azure SQL with Private Endpoint and Test Connectivity from AKS</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Sun, 08 Mar 2026 07:14:08 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/deploy-azure-sql-with-private-endpoint-and-test-connectivity-from-aks-21m0</link>
      <guid>https://forem.com/pilgrim2go/deploy-azure-sql-with-private-endpoint-and-test-connectivity-from-aks-21m0</guid>
      <description>&lt;p&gt;This tutorial demonstrates how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create networking for AKS&lt;/li&gt;
&lt;li&gt;Deploy an AKS cluster using &lt;strong&gt;Azure CNI Overlay&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Deploy &lt;strong&gt;Azure SQL Database&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Secure it using a &lt;strong&gt;Private Endpoint&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fix DNS resolution using &lt;strong&gt;Private DNS Zone&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verify connectivity from Kubernetes using debugging pods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to allow &lt;strong&gt;AKS pods to securely connect to Azure SQL using private networking only&lt;/strong&gt;.&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%2F29b36kubjlnd42rx6qru.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%2F29b36kubjlnd42rx6qru.png" alt="Diagram" width="687" height="833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Define Environment Variables
&lt;/h2&gt;

&lt;p&gt;First define reusable variables.&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;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;southeastasia

&lt;span class="nv"&gt;RG_NETWORK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg-aks-network
&lt;span class="nv"&gt;RG_OVERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg-aks-overlay
&lt;span class="nv"&gt;RG_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg-aks-underlay

&lt;span class="nv"&gt;VNET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vnet-aks-lab

&lt;span class="nv"&gt;SUBNET_OVERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;subnet-overlay
&lt;span class="nv"&gt;SUBNET_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;subnet-underlay

&lt;span class="nv"&gt;AKS_OVERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aks-overlay
&lt;span class="nv"&gt;AKS_UNDERLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aks-underlay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Create Resource Groups
&lt;/h2&gt;

&lt;p&gt;Create resource groups to organize infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az group create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt;
az group create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RG_OVERLAY&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt;
az group create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$RG_UNDERLAY&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Create Virtual Network
&lt;/h2&gt;

&lt;p&gt;Create a VNet that will host:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AKS nodes&lt;/li&gt;
&lt;li&gt;Private endpoints&lt;/li&gt;
&lt;li&gt;Services
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.0.0/16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Create Subnets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overlay subnet (AKS nodes)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet subnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.1.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Underlay subnet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet subnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.2.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve subnet IDs:&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;OVERLAY_SUBNET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az network vnet subnet show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;UNDERLAY_SUBNET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az network vnet subnet show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SUBNET_UNDERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$OVERLAY_SUBNET_ID&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$UNDERLAY_SUBNET_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Deploy AKS Cluster (Azure CNI Overlay)
&lt;/h2&gt;

&lt;p&gt;Create the AKS cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$AKS_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-count&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-plugin&lt;/span&gt; azure &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-plugin-mode&lt;/span&gt; overlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pod-cidr&lt;/span&gt; 192.168.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-cidr&lt;/span&gt; 172.16.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dns-service-ip&lt;/span&gt; 172.16.0.10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-subnet-id&lt;/span&gt; &lt;span class="nv"&gt;$OVERLAY_SUBNET_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--generate-ssh-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Connect kubectl to the Cluster
&lt;/h2&gt;

&lt;p&gt;Retrieve credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks get-credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_OVERLAY&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$AKS_OVERLAY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify cluster 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 nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide
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;
  
  
  7. Create Subnets for Services
&lt;/h2&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet subnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; subnet-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.3.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Private Endpoint subnet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network vnet subnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; subnet-private-endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--address-prefix&lt;/span&gt; 10.0.4.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Create Azure SQL Server
&lt;/h2&gt;

&lt;p&gt;Generate a unique name.&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;SQL_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aks-lab-sql-&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create SQL server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az sql server create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SQL_SERVER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nv"&gt;$LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--admin-user&lt;/span&gt; sqladmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--admin-password&lt;/span&gt; &lt;span class="s1"&gt;'StrongPass123!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure provider is registered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az provider register &lt;span class="nt"&gt;--namespace&lt;/span&gt; Microsoft.Sql
az provider show &lt;span class="nt"&gt;--namespace&lt;/span&gt; Microsoft.Sql &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"registrationState"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Create SQL Database
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az sql db create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--server&lt;/span&gt; &lt;span class="nv"&gt;$SQL_SERVER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; demo-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-objective&lt;/span&gt; Basic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the SQL resource ID.&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;SQL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az sql server show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$SQL_SERVER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$SQL_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Create Private Endpoint for Azure SQL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-endpoint create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sql-private-endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vnet-name&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt; subnet-private-endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--private-connection-resource-id&lt;/span&gt; &lt;span class="nv"&gt;$SQL_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-id&lt;/span&gt; sqlServer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--connection-name&lt;/span&gt; sqlConnection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. Verify Private Endpoint IP
&lt;/h2&gt;

&lt;p&gt;Get network interface ID.&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;NIC_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az network private-endpoint show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sql-private-endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"networkInterfaces[0].id"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$NIC_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve private IP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network nic show &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ids&lt;/span&gt; &lt;span class="nv"&gt;$NIC_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"ipConfigurations[0].privateIPAddress"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; tsv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.0.4.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Test Network from AKS Pod
&lt;/h2&gt;

&lt;p&gt;Launch a debugging pod.&lt;/p&gt;

&lt;p&gt;Image:&lt;br&gt;
nicolaka/netshoot&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl run net-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nicolaka/netshoot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the pod you can test DNS and networking.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Test SQL Client Pod
&lt;/h2&gt;

&lt;p&gt;Run a SQL client container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl run sql-client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mcr.microsoft.com/mssql-tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  14. Create Private DNS Zone
&lt;/h2&gt;

&lt;p&gt;Azure SQL private endpoints require DNS mapping.&lt;/p&gt;

&lt;p&gt;Create the private DNS zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-dns zone create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; privatelink.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Link the DNS zone to the VNet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-dns &lt;span class="nb"&gt;link &lt;/span&gt;vnet create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone-name&lt;/span&gt; privatelink.database.windows.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sql-dns-link &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--virtual-network&lt;/span&gt; &lt;span class="nv"&gt;$VNET_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--registration-enabled&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach the private endpoint to the DNS zone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az network private-endpoint dns-zone-group create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &lt;span class="nv"&gt;$RG_NETWORK&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--endpoint-name&lt;/span&gt; sql-private-endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sql-zone-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--private-dns-zone&lt;/span&gt; privatelink.database.windows.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone-name&lt;/span&gt; sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  15. Verify DNS Resolution from AKS
&lt;/h2&gt;

&lt;p&gt;Run another debug 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 run net-test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nicolaka/netshoot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test DNS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup &amp;lt;your-sql-server&amp;gt;.database.windows.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.0.4.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  16. Connect to Azure SQL
&lt;/h2&gt;

&lt;p&gt;Run SQL client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl run sql-client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mcr.microsoft.com/mssql-tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect using &lt;code&gt;sqlcmd&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlcmd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-S&lt;/span&gt; aks-lab-sql-31445.database.windows.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-U&lt;/span&gt; sqladmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s1"&gt;'StrongPass123!'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; demo-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If successful you will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means you are connected to the SQL server.&lt;/p&gt;

&lt;p&gt;Example query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AKS Pod
   │
   │ DNS Query
   ▼
Private DNS Zone
(privatelink.database.windows.net)
   │
   ▼
Private Endpoint (10.0.4.4)
   │
   ▼
Azure SQL Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;✅ You now have &lt;strong&gt;Azure SQL accessible privately from AKS pods&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>aks</category>
      <category>azure</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to Permanently Disable a systemd Service on Ubuntu</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Thu, 05 Mar 2026 13:45:24 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/how-to-permanently-disable-a-systemd-service-on-ubuntu-noh</link>
      <guid>https://forem.com/pilgrim2go/how-to-permanently-disable-a-systemd-service-on-ubuntu-noh</guid>
      <description>&lt;p&gt;Sometimes you install software that registers itself as a &lt;strong&gt;systemd service&lt;/strong&gt; and starts automatically at boot. If you don’t want it running anymore, you can disable or even mask it so it never starts again. Here’s a step-by-step guide using &lt;code&gt;vllm.service&lt;/code&gt; as an example.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Stop the Service Immediately
&lt;/h2&gt;

&lt;p&gt;First, stop the service if it’s currently 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="nb"&gt;sudo &lt;/span&gt;systemctl stop vllm.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Disable the Service at Boot
&lt;/h2&gt;

&lt;p&gt;Prevent the service from starting automatically on reboot:&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;sudo &lt;/span&gt;systemctl disable vllm.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes the symlink from the systemd startup sequence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Mask the Service (Optional but Stronger)
&lt;/h2&gt;

&lt;p&gt;Masking a service ensures it &lt;strong&gt;cannot be started manually or by another dependency&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
If the unit file already exists in &lt;code&gt;/etc/systemd/system/&lt;/code&gt;, you’ll need to rename or remove it before masking:&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;sudo mv&lt;/span&gt; /etc/systemd/system/vllm.service /etc/systemd/system/vllm.service.backup
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl mask vllm.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Verify the Status
&lt;/h2&gt;

&lt;p&gt;Check whether the service is disabled or masked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl is-enabled vllm.service
systemctl status vllm.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;disabled&lt;/code&gt; → won’t start at boot
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;masked&lt;/code&gt; → cannot be started at all
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: (Optional) Re-enable Later
&lt;/h2&gt;

&lt;p&gt;If you change your mind, simply unmask and re-enable:&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;sudo &lt;/span&gt;systemctl unmask vllm.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;vllm.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disable&lt;/strong&gt; if you just don’t want it auto-starting.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mask&lt;/strong&gt; if you want to block it completely.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove the unit file&lt;/strong&gt; if you’re sure you’ll never use it again.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach ensures the service stays off permanently, even after rebooting.  &lt;/p&gt;

</description>
      <category>sysadmin</category>
      <category>devops</category>
      <category>systemd</category>
    </item>
    <item>
      <title>Troubleshooting KVM Issues</title>
      <dc:creator>iapilgrim</dc:creator>
      <pubDate>Thu, 05 Mar 2026 13:44:18 +0000</pubDate>
      <link>https://forem.com/pilgrim2go/troubleshooting-kvm-issues-5cn1</link>
      <guid>https://forem.com/pilgrim2go/troubleshooting-kvm-issues-5cn1</guid>
      <description>&lt;h1&gt;
  
  
  🧱 1️⃣ Initial Symptom
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ❌ “Booting from hard disk…” (hangs)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What This Means Technically
&lt;/h3&gt;

&lt;p&gt;Firmware attempted to boot from the virtual disk but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could not find a valid bootloader&lt;/li&gt;
&lt;li&gt;Or could not understand the partition scheme&lt;/li&gt;
&lt;li&gt;Or firmware mode didn’t match OS install mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This message comes from firmware (not Linux).&lt;/p&gt;

&lt;p&gt;In KVM environments, firmware is provided by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legacy BIOS (SeaBIOS)&lt;/li&gt;
&lt;li&gt;UEFI (OVMF from TianoCore)&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🔎 2️⃣ First Investigation: Is the Disk Corrupted?
&lt;/h1&gt;

&lt;p&gt;We checked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qemu-img info
virt-filesystems
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What We Found
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;qcow2 format valid&lt;/li&gt;
&lt;li&gt;Not corrupted&lt;/li&gt;
&lt;li&gt;GPT layout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/dev/sda1&lt;/code&gt; → vfat (511M)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/dev/sda2&lt;/code&gt; → ext4&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/dev/sda3&lt;/code&gt; → swap&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Critical Clue
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;vfat&lt;/code&gt; EFI partition = &lt;strong&gt;UEFI installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That immediately ruled out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk corruption&lt;/li&gt;
&lt;li&gt;Missing root filesystem&lt;/li&gt;
&lt;li&gt;Missing GRUB files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt; Always inspect disk structure before guessing.&lt;/p&gt;




&lt;h1&gt;
  
  
  ⚡ 3️⃣ Root Cause #1 — Firmware Mismatch
&lt;/h1&gt;

&lt;p&gt;Your VM XML showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;os&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;type&lt;/span&gt; &lt;span class="na"&gt;arch=&lt;/span&gt;&lt;span class="s"&gt;'x86_64'&lt;/span&gt; &lt;span class="na"&gt;machine=&lt;/span&gt;&lt;span class="s"&gt;'pc-q35-9.2'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;hvm&lt;span class="nt"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;boot&lt;/span&gt; &lt;span class="na"&gt;dev=&lt;/span&gt;&lt;span class="s"&gt;'hd'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/os&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;&amp;lt;loader&amp;gt;&lt;/code&gt; entry → That means &lt;strong&gt;legacy BIOS mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But your disk layout proved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installed using UEFI&lt;/li&gt;
&lt;li&gt;GPT partition table&lt;/li&gt;
&lt;li&gt;EFI System Partition&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why BIOS Could Not Boot
&lt;/h3&gt;

&lt;p&gt;Legacy BIOS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Looks for MBR&lt;/li&gt;
&lt;li&gt;Expects GRUB in MBR stage1&lt;/li&gt;
&lt;li&gt;Does not understand EFI partitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UEFI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads FAT EFI partition&lt;/li&gt;
&lt;li&gt;Loads &lt;code&gt;.efi&lt;/code&gt; binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So BIOS firmware had no idea how to boot your disk.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
UEFI install + BIOS firmware = guaranteed boot failure.&lt;/p&gt;

&lt;p&gt;This is extremely common in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;KVM&lt;/li&gt;
&lt;li&gt;Proxmox VE&lt;/li&gt;
&lt;li&gt;OpenStack&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  🔧 4️⃣ Fix #1 — Install OVMF (UEFI Firmware)
&lt;/h1&gt;

&lt;p&gt;Your system didn’t even have OVMF installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/share/OVMF/OVMF_CODE.fd → missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We installed firmware package and found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OVMF_CODE_4M.fd
OVMF_VARS_4M.fd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then updated VM XML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;loader&lt;/span&gt; &lt;span class="na"&gt;readonly=&lt;/span&gt;&lt;span class="s"&gt;'yes'&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'pflash'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  /usr/share/OVMF/OVMF_CODE_4M.fd
&lt;span class="nt"&gt;&amp;lt;/loader&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nvram&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/nvram&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now firmware matched disk layout.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
Hypervisor provides firmware. OS depends on firmware type.&lt;/p&gt;


&lt;h1&gt;
  
  
  🧨 5️⃣ New Symptom — UEFI Shell Appeared
&lt;/h1&gt;

&lt;p&gt;After switching to UEFI, VM booted into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shell&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;UEFI firmware is working&lt;/li&gt;
&lt;li&gt;EFI partition exists&lt;/li&gt;
&lt;li&gt;But no boot entry in NVRAM&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  🧠 Why That Happened
&lt;/h1&gt;

&lt;p&gt;When you switched firmware modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New NVRAM file was created&lt;/li&gt;
&lt;li&gt;It had no stored boot entries&lt;/li&gt;
&lt;li&gt;So firmware didn't know about &lt;code&gt;/EFI/debian/grubx64.efi&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is expected behavior.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
UEFI boot depends on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EFI partition&lt;/li&gt;
&lt;li&gt;Bootloader file&lt;/li&gt;
&lt;li&gt;NVRAM boot entry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three must exist.&lt;/p&gt;


&lt;h1&gt;
  
  
  🛠 6️⃣ Manual Boot from EFI Shell
&lt;/h1&gt;

&lt;p&gt;Inside shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fs0:
cd EFI\debian
grubx64.efi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GRUB launched → Debian booted.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Disk is healthy&lt;/li&gt;
&lt;li&gt;GRUB installed correctly&lt;/li&gt;
&lt;li&gt;Root filesystem intact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
UEFI shell is a powerful debugging tool.&lt;/p&gt;


&lt;h1&gt;
  
  
  🔐 7️⃣ Root Password Problem
&lt;/h1&gt;

&lt;p&gt;There is &lt;strong&gt;no default root password in Debian&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In modern Debian:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you leave root password blank → root login disabled&lt;/li&gt;
&lt;li&gt;You must use sudo user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You chose to reset password offline using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;guestmount + chroot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Mount qcow2 offline&lt;/li&gt;
&lt;li&gt;Enter filesystem&lt;/li&gt;
&lt;li&gt;Modify system safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
Virtual disks are just files.&lt;br&gt;
You can perform offline surgery safely.&lt;/p&gt;


&lt;h1&gt;
  
  
  🏁 8️⃣ Permanent Fix — Register GRUB Properly
&lt;/h1&gt;

&lt;p&gt;After booting successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grub-install
update-grub
efibootmgr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Boot0003* debian
BootOrder: 0003,...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;GRUB installed to EFI partition&lt;/li&gt;
&lt;li&gt;UEFI boot entry created&lt;/li&gt;
&lt;li&gt;Boot order corrected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now VM boots automatically.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Lesson:&lt;/strong&gt;&lt;br&gt;
UEFI requires proper NVRAM registration.&lt;/p&gt;


&lt;h1&gt;
  
  
  📚 Full Timeline of Issues
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Fix&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;Hang at boot&lt;/td&gt;
&lt;td&gt;Firmware mismatch&lt;/td&gt;
&lt;td&gt;Enable UEFI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;OVMF missing&lt;/td&gt;
&lt;td&gt;Firmware not installed&lt;/td&gt;
&lt;td&gt;Install ovmf&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;XML validation errors&lt;/td&gt;
&lt;td&gt;Wrong schema structure&lt;/td&gt;
&lt;td&gt;Correct &lt;code&gt;&amp;lt;os&amp;gt;&lt;/code&gt; block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;UEFI shell&lt;/td&gt;
&lt;td&gt;No NVRAM boot entry&lt;/td&gt;
&lt;td&gt;Manual grub launch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Root login fail&lt;/td&gt;
&lt;td&gt;No default password&lt;/td&gt;
&lt;td&gt;Reset via chroot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Manual boot required&lt;/td&gt;
&lt;td&gt;Boot entry not registered&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grub-install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h1&gt;
  
  
  🧠 Core Concepts You Mastered
&lt;/h1&gt;
&lt;h2&gt;
  
  
  🔹 Firmware Layers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;BIOS vs UEFI&lt;/li&gt;
&lt;li&gt;SeaBIOS vs OVMF&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔹 Disk Layout
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GPT vs MBR&lt;/li&gt;
&lt;li&gt;EFI partition&lt;/li&gt;
&lt;li&gt;Root partition&lt;/li&gt;
&lt;li&gt;Swap&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔹 libvirt XML
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;loader&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;nvram&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Machine type (q35)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔹 UEFI Mechanics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;EFI shell&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.efi&lt;/code&gt; bootloaders&lt;/li&gt;
&lt;li&gt;NVRAM boot entries&lt;/li&gt;
&lt;li&gt;&lt;code&gt;efibootmgr&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔹 Virtual Disk Forensics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;qemu-img&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;virt-filesystems&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;guestmount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;chroot repairs&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  🚀 Biggest Takeaway
&lt;/h1&gt;

&lt;p&gt;The problem was &lt;strong&gt;never the OS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It was the relationship between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Firmware
↕
Bootloader
↕
Partition scheme
↕
Kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In virtualization, firmware mismatches are one of the most common boot failures.&lt;/p&gt;

&lt;p&gt;Now you understand that chain completely.&lt;/p&gt;




&lt;h1&gt;
  
  
  🏆 What Level You’re At Now
&lt;/h1&gt;

&lt;p&gt;You moved from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“VM won’t boot”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Debugging firmware, UEFI NVRAM, partition schemes, and GRUB inside KVM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s real infrastructure-level troubleshooting.&lt;/p&gt;

</description>
      <category>kvm</category>
      <category>troubleshooting</category>
    </item>
  </channel>
</rss>
