<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: The Cyber Sidekick</title>
    <description>The latest articles on DEV Community by The Cyber Sidekick (@thecybersidekick).</description>
    <link>https://dev.to/thecybersidekick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3987699%2F79b4c7af-5633-4f83-a6d5-03651461b293.png</url>
      <title>DEV Community: The Cyber Sidekick</title>
      <link>https://dev.to/thecybersidekick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thecybersidekick"/>
    <language>en</language>
    <item>
      <title>Install Argo CD with Helm, and survive the missing-CRD trap (CKA)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:38:38 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/install-argo-cd-with-helm-and-survive-the-missing-crd-trap-cka-4ph7</link>
      <guid>https://dev.to/thecybersidekick/install-argo-cd-with-helm-and-survive-the-missing-crd-trap-cka-4ph7</guid>
      <description>&lt;h2&gt;
  
  
  Install Argo CD with Helm (and survive the missing-CRD trap)
&lt;/h2&gt;

&lt;p&gt;This exam question looks like three Helm commands: add a repository, render a template, install a release. And it hands you a comforting line: the Argo CD CRDs have already been pre-installed in the cluster. In this lab that line is false, on purpose, and what happens next teaches you exactly what &lt;code&gt;crds.install=false&lt;/code&gt; really means. Let's run it.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=kYcHmIh_eHw" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=kYcHmIh_eHw&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Cluster Architecture, Installation &amp;amp; Configuration walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;Here is the question. Add the official Argo CD Helm repository to the cluster under the name argo; the URL is given in the question, you never have to memorize repository links. The Argo CD CRDs, it says, have already been pre-installed. Generate a Helm template for release argocd, chart version 7.7.3, in the argocd namespace, save it to argo-helm.yaml, and configure the chart to not install the CRDs. Then install the release with the same name, version and namespace, again without CRDs. And one thing you do not have to do: configure access to the Argo CD server UI. Three commands, one premise. Remember the premise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the official Argo CD Helm repo as 'argo' (URL is given in the question)&lt;/li&gt;
&lt;li&gt;Premise: the Argo CD CRDs 'have already been pre-installed in the cluster'&lt;/li&gt;
&lt;li&gt;helm template: release argocd, chart version 7.7.3, ns argocd -&amp;gt; argo-helm.yaml, NO CRDs&lt;/li&gt;
&lt;li&gt;helm install: same release/version/namespace, NO CRDs; the server UI is out of scope&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the Helm flow works
&lt;/h2&gt;

&lt;p&gt;Three Helm verbs do the work. helm repo add registers where charts come from, under a name you choose. helm template renders the chart into plain YAML on your machine; nothing touches the cluster, which is why the question can ask you to save the output to a file. helm install submits that same render to the cluster as a managed release. The flag that ties them together is set &lt;code&gt;crds.install=false&lt;/code&gt;. The argo-cd chart bundles the Argo CD custom resource definitions, and when a question says the CRDs are managed separately, your job is to keep the chart's hands off them, in the template and in the install. Keep the two commands identical apart from the verb; if they drift, the file you saved describes a different system than the one you deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the argo repo
&lt;/h2&gt;

&lt;p&gt;Step one. helm repo add argo, then the URL from the question. Helm fetches the repository index immediately, so a mistyped URL fails right here instead of at install time. helm repo list confirms the repository is registered under exactly the name the question asked for: argo.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm repo add argo https://argoproj.github.io/argo-helm
&lt;span class="go"&gt;"argo" has been added to your repositories

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm repo list
&lt;span class="go"&gt;NAME        URL                                     
argo        https://argoproj.github.io/argo-helm
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Render the template
&lt;/h2&gt;

&lt;p&gt;Step two, the template. Release name argocd, chart &lt;code&gt;argo/argo-cd&lt;/code&gt;, version 7.7.3 pinned exactly, namespace argocd, &lt;code&gt;crds.install&lt;/code&gt; set to false, and the whole thing redirected into argo-helm.yaml. The command prints nothing because the entire render went into the file, and it is substantial: over three thousand lines of YAML. Here is the check that proves the flag worked: grep the file for kind CustomResourceDefinition. Zero. This chart normally ships three CRDs; with the flag set, the render contains none of them.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm template argocd argo/argo-cd &lt;span class="nt"&gt;--version&lt;/span&gt; 7.7.3 &lt;span class="nt"&gt;--namespace&lt;/span&gt; argocd &lt;span class="nt"&gt;--set&lt;/span&gt; crds.install&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; argo-helm.yaml
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; argo-helm.yaml
&lt;span class="go"&gt;3057 argo-helm.yaml

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'kind: CustomResourceDefinition'&lt;/span&gt; argo-helm.yaml
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  helm install
&lt;/h2&gt;

&lt;p&gt;Step three is the same command with the verb swapped: helm install, same release name, same chart, same version, same namespace, same &lt;code&gt;crds.install&lt;/code&gt; false. Helm prints the release notes, and helm ls in the argocd namespace shows release argocd, revision one, status deployed, chart argo-cd 7.7.3, app version 2.13.0. As far as Helm is concerned, the job is done. The cluster is about to disagree.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;argocd argo/argo-cd &lt;span class="nt"&gt;--version&lt;/span&gt; 7.7.3 &lt;span class="nt"&gt;--namespace&lt;/span&gt; argocd &lt;span class="nt"&gt;--set&lt;/span&gt; crds.install&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="go"&gt;NAME: argocd
LAST DEPLOYED: Mon Jul 13 09:49:46 2026
NAMESPACE: argocd
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;span class="go"&gt;NAME    NAMESPACE   REVISION    UPDATED                                 STATUS      CHART           APP VERSION
argocd  argocd      1           2026-07-13 09:49:46.583903829 -0400 EDT deployed    argo-cd-7.7.3   v2.13.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The crashloop
&lt;/h2&gt;

&lt;p&gt;Give the pods a moment and look again. Redis and dex are up, but the server and the applicationset controller are already in a crash loop, and the application controller never goes ready; everything that watches Argo CD resources is failing. Pull the logs from the server and the last line says exactly why: level fatal, the server could not find the requested resource. That is a component asking the API server for a resource type that does not exist. We told the chart not to install the CRDs because the question said they were pre-installed. They are not. The premise was false, and Helm had no way to know.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;span class="go"&gt;NAME                                               READY   STATUS             RESTARTS      AGE
argocd-application-controller-0                    0/1     Running            0             84s
argocd-applicationset-controller-bcdc99fcf-jdfj9   0/1     CrashLoopBackOff   3 (41s ago)   84s
argocd-dex-server-77f8fcf9d9-plbt8                 1/1     Running            0             84s
argocd-notifications-controller-7769fd5fd-rdqzh    1/1     Running            0             84s
argocd-redis-768545f6f-rjsgq                       1/1     Running            0             84s
argocd-repo-server-fd74968b8-dr772                 1/1     Running            0             84s
argocd-server-74b5fbf858-cn5t4                     0/1     CrashLoopBackOff   4 (4s ago)    84s

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl logs deploy/argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;span class="go"&gt;time="2026-07-13T13:51:10Z" level=info msg="Starting configmap/secret informers"
time="2026-07-13T13:51:11Z" level=info msg="Configmap/secret informer synced"
time="2026-07-13T13:51:11Z" level=fatal msg="the server could not find the requested resource (post appprojects.argoproj.io)"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The missing CRDs
&lt;/h2&gt;

&lt;p&gt;Confirm the diagnosis in two seconds: kubectl get crd, grep argoproj, nothing. So we install the CRDs ourselves, but pinned to the right version, because CRDs and controllers drift apart across releases. The chart's appVersion tells you which Argo CD this chart deploys: helm show chart says 7.7.3 ships version 2.13.0. Apply the three CRD manifests from the Argo CD repository at exactly that tag: applications, applicationsets, and appprojects. Now the same grep finds all three.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get crd | &lt;span class="nb"&gt;grep &lt;/span&gt;argoproj
&lt;span class="go"&gt;No resources found

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;helm show chart argo/argo-cd &lt;span class="nt"&gt;--version&lt;/span&gt; 7.7.3 | &lt;span class="nb"&gt;grep &lt;/span&gt;appVersion
&lt;span class="go"&gt;appVersion: v2.13.0
version: 7.7.3

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; crds/
&lt;span class="go"&gt;customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/applicationsets.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get crd | &lt;span class="nb"&gt;grep &lt;/span&gt;argoproj
&lt;span class="go"&gt;applications.argoproj.io      2026-07-13T13:51:16Z
applicationsets.argoproj.io   2026-07-13T13:51:16Z
appprojects.argoproj.io       2026-07-13T13:51:16Z
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restart + verify
&lt;/h2&gt;

&lt;p&gt;The crashlooped pods would eventually recover on their own as the backoff retries, but do not sit and wait in an exam. One rollout restart of the workloads in the namespace brings fresh pods up immediately, and this time every informer finds its CRDs. A minute later the whole namespace is Running and Ready. That is the full question: repository added as argo, template saved with zero CRDs in it, release installed at 7.7.3, and a healthy Argo CD. And notice what the fix was not: we never touched the Helm release. The release was correct; the environment broke the promise.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd rollout restart deployment
&lt;span class="go"&gt;deployment.apps/argocd-applicationset-controller restarted
deployment.apps/argocd-dex-server restarted
deployment.apps/argocd-notifications-controller restarted
deployment.apps/argocd-redis restarted
deployment.apps/argocd-repo-server restarted
deployment.apps/argocd-server restarted

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;span class="go"&gt;NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   0          11s
argocd-applicationset-controller-547b7778c8-psvtd   1/1     Running   0          58s
argocd-dex-server-65cd9bfbb8-ptbnj                  1/1     Running   0          58s
argocd-notifications-controller-76fcfb8864-6s7sb    1/1     Running   0          58s
argocd-redis-6d9cb5f875-6z8xw                       1/1     Running   0          58s
argocd-repo-server-87cf9c697-dcqzx                  1/1     Running   0          58s
argocd-server-6f7b69c655-wgrhn                      1/1     Running   0          58s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps. The repository URL and the chart version are printed in the question; copy them, pin &lt;code&gt;--version&lt;/code&gt; exactly, and never guess. Template and install must carry identical values: same namespace, same set &lt;code&gt;crds.install=false&lt;/code&gt;, only the verb changes. When a question states a premise like CRDs are pre-installed, verify it, it costs two seconds: kubectl get crd, grep argoproj. And learn the signature: a controller crashlooping with the server could not find the requested resource means a missing CRD, and the chart's appVersion tells you exactly which CRD version to apply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The repo URL + chart version are IN the question: copy them, pin --version exactly&lt;/li&gt;
&lt;li&gt;template and install take identical values; only the verb changes&lt;/li&gt;
&lt;li&gt;Premises are verifiable: 'CRDs pre-installed' -&amp;gt; kubectl get crd | grep argoproj (2 seconds)&lt;/li&gt;
&lt;li&gt;'could not find the requested resource' = missing CRD; match it to the chart's appVersion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;helm repo add argo -&amp;gt; helm template -&amp;gt; helm install, pinned to 7.7.3, crds.install=false on both&lt;/li&gt;
&lt;li&gt;helm ls said 'deployed'; the pods crashlooped: Helm status is not health&lt;/li&gt;
&lt;li&gt;Missing argoproj CRDs -&amp;gt; 'could not find the requested resource'; apply CRDs at appVersion v2.13.0&lt;/li&gt;
&lt;li&gt;rollout restart, all pods Running; subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario10-argocd-helm-install
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>helm</category>
      <category>argocd</category>
    </item>
    <item>
      <title>Install Calico and prove NetworkPolicy enforcement (CKA Services &amp; Networking)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:09:06 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/install-calico-and-prove-networkpolicy-enforcement-cka-services-networking-28k6</link>
      <guid>https://dev.to/thecybersidekick/install-calico-and-prove-networkpolicy-enforcement-cka-services-networking-28k6</guid>
      <description>&lt;h2&gt;
  
  
  Install Calico and prove NetworkPolicy enforcement
&lt;/h2&gt;

&lt;p&gt;The exam says install and configure a CNI of your choice, flannel or Calico, and then adds one line that makes the choice for you: it must support NetworkPolicy enforcement. flannel does not enforce policies, so this is a Calico question. Let's install it from the manifests, verify it, and prove a policy actually blocks traffic.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=7Eaf7Wogsa4" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=7Eaf7Wogsa4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Services &amp;amp; Networking walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;Here is the setup. A fresh cluster has no CNI, so its nodes are NotReady and CoreDNS is Pending. The question offers flannel or Calico, says install from manifest files, do not use Helm, and lists three requirements: the CNI is properly installed and configured, pods can communicate with each other, and it supports NetworkPolicy enforcement. That last requirement is the trap. flannel gives you pod networking but silently ignores NetworkPolicy objects. Only Calico satisfies all three.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fresh cluster with NO CNI: nodes NotReady, CoreDNS Pending&lt;/li&gt;
&lt;li&gt;Install a CNI of your choice (flannel or Calico) from manifests, no Helm&lt;/li&gt;
&lt;li&gt;Pods must communicate with each other, and the CNI must be properly installed&lt;/li&gt;
&lt;li&gt;'Must support NetworkPolicy enforcement' =&amp;gt; flannel is out, Calico is the answer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the Calico operator install works
&lt;/h2&gt;

&lt;p&gt;Calico's manifest install is operator based and comes in three parts. First the operator CRDs, then the Tigera operator itself, a deployment that knows how to install and manage Calico. Then you hand the operator an Installation resource describing the Calico you want, and it deploys everything into the calico-system namespace to match. Two things to know before you touch the keyboard. The Installation's IP pool cidr must agree with the cluster's pod CIDR, which is 192.168.0.0/16 here. And these manifests need kubectl create, not apply: the CRD file is bigger than the annotation kubectl apply attaches, so apply fails on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  No CNI: nodes NotReady
&lt;/h2&gt;

&lt;p&gt;Start by confirming the starting state. Every node is NotReady, the classic symptom of a missing CNI. And listing pods across all namespaces shows two things: CoreDNS is Pending because there is no pod network for it to join, and there are no calico or flannel pods anywhere, so no CNI is installed yet. This is a fresh cluster, and the network plugin is ours to install.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;span class="go"&gt;NAME                          STATUS     ROLES           AGE   VERSION
cka-scenario9-control-plane   NotReady   control-plane   8s    v1.36.1
&lt;/span&gt;&lt;span class="gp"&gt;cka-scenario9-worker          NotReady   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;0s    v1.36.1
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;span class="go"&gt;NAMESPACE            NAME                                                  READY   STATUS    RESTARTS   AGE
kube-system          coredns-589f44dc88-522x8                              0/1     Pending   0          1s
kube-system          coredns-589f44dc88-zsxh2                              0/1     Pending   0          1s
kube-system          etcd-cka-scenario9-control-plane                      0/1     Running   0          8s
kube-system          kube-apiserver-cka-scenario9-control-plane            1/1     Running   0          8s
kube-system          kube-controller-manager-cka-scenario9-control-plane   1/1     Running   0          8s
kube-system          kube-proxy-ds9pp                                      1/1     Running   0          1s
kube-system          kube-proxy-lwx6m                                      0/1     Pending   0          3s
kube-system          kube-scheduler-cka-scenario9-control-plane            0/1     Running   0          8s
local-path-storage   local-path-provisioner-855c7b7774-75cbk               0/1     Pending   0          1s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the CRDs + operator
&lt;/h2&gt;

&lt;p&gt;Install the operator pieces with kubectl create. The first file registers the custom resource definitions, thirty two of them. The second deploys the Tigera operator itself. Note the verb: create, not apply. Some of these CRDs are larger than the last-applied annotation kubectl apply wants to attach, so apply fails on this file, and the Calico docs say create for exactly that reason. A few seconds later the operator pod is Running in the tigera-operator namespace, waiting for us to tell it what to install.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; operator-crds.yaml
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagedglobalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagedkubernetesnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/stagednetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/tiers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/adminnetworkpolicies.policy.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/baselineadminnetworkpolicies.policy.networking.k8s.io created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; tigera-operator.yaml
&lt;span class="go"&gt;namespace/tigera-operator created
serviceaccount/tigera-operator created
clusterrole.rbac.authorization.k8s.io/tigera-operator-secrets created
clusterrole.rbac.authorization.k8s.io/tigera-operator created
clusterrolebinding.rbac.authorization.k8s.io/tigera-operator created
rolebinding.rbac.authorization.k8s.io/tigera-operator-secrets created
deployment.apps/tigera-operator created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; tigera-operator
&lt;span class="go"&gt;NAME                               READY   STATUS    RESTARTS   AGE
tigera-operator-696d7c8fc4-59bf5   1/1     Running   0          4s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Installation resource
&lt;/h2&gt;

&lt;p&gt;Now describe the Calico you want. This Installation resource is the heart of the stock custom-resources file, and the field that matters is the IP pool cidr: 192.168.0.0/16, matching this cluster's pod CIDR. If those two disagree you get the same class of pain as a flannel CIDR mismatch, so always check before you create it. Create it and the operator takes over, pulling Calico up piece by piece. The tigerastatus resource is your progress bar: when every row reports Available True, the install is done.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;custom-resources.yaml
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;The Installation resource: the Tigera operator watches &lt;span class="k"&gt;for &lt;/span&gt;this and deploys Calico
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to match it. The ipPool cidr MUST agree with the cluster&lt;span class="s1"&gt;'s pod CIDR (192.168.0.0/16).
&lt;/span&gt;&lt;span class="go"&gt;apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    ipPools:
      - cidr: 192.168.0.0/16
        encapsulation: VXLANCrossSubnet

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; custom-resources.yaml
&lt;span class="go"&gt;installation.operator.tigera.io/default created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get tigerastatus
&lt;span class="go"&gt;NAME      AVAILABLE   PROGRESSING   DEGRADED   SINCE
calico    True        False         False      0s
ippools   True        False         False      55s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Calico up, nodes Ready
&lt;/h2&gt;

&lt;p&gt;Trust but verify. In calico-system there is a calico-node pod on every node, that is the dataplane that wires up pods and enforces policy, plus typha and the kube-controllers, all Running. And the payoff: both nodes have flipped to Ready, and CoreDNS is finally scheduled. The CNI is properly installed and configured, which was requirement one.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; calico-system &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="go"&gt;NAME                                       READY   STATUS    RESTARTS   AGE   IP                NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;calico-kube-controllers-6c8496f5c8-flpxp   1/1     Running   0          63s   192.168.227.197   cka-scenario9-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;calico-node-dpqqj                          1/1     Running   0          63s   172.18.0.15       cka-scenario9-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;calico-node-pdgjt                          1/1     Running   0          63s   172.18.0.16       cka-scenario9-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;calico-typha-6f8c54fdf6-mnvnn              1/1     Running   0          63s   172.18.0.15       cka-scenario9-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;csi-node-driver-6mlr5                      2/2     Running   0          63s   192.168.227.194   cka-scenario9-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;csi-node-driver-ntcxl                      2/2     Running   0          63s   192.168.178.65    cka-scenario9-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;span class="go"&gt;NAME                          STATUS   ROLES           AGE   VERSION
cka-scenario9-control-plane   Ready    control-plane   82s   v1.36.1
&lt;/span&gt;&lt;span class="gp"&gt;cka-scenario9-worker          Ready    &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;73s   v1.36.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prove pods can talk
&lt;/h2&gt;

&lt;p&gt;Requirement two: pods must communicate with each other. Create two test pods pinned to different nodes and list them wide, each gets an IP from 192.168.0.0/16 on a different host. Then exec into test one and ping test two's pod IP: replies come back across the nodes, so pod-to-pod networking works end to end. One practical note: ping the IP, not the pod name. Bare pods do not get DNS records, so a name lookup fails with bad address even on a perfectly healthy network.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; connectivity-test.yaml
&lt;span class="go"&gt;pod/test1 created
pod/test2 created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;conn-test
&lt;span class="go"&gt;NAME    READY   STATUS    RESTARTS   AGE   IP                NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;test1   1/1     Running   0          3s    192.168.178.66    cka-scenario9-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;test2   1/1     Running   0          3s    192.168.227.198   cka-scenario9-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;test1 &lt;span class="nt"&gt;--&lt;/span&gt; ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 &amp;lt;test2-ip&amp;gt;
&lt;span class="go"&gt;PING 192.168.227.198 (192.168.227.198): 56 data bytes
64 bytes from 192.168.227.198: seq=0 ttl=62 time=0.351 ms
64 bytes from 192.168.227.198: seq=1 ttl=62 time=0.091 ms
64 bytes from 192.168.227.198: seq=2 ttl=62 time=0.100 ms

--- 192.168.227.198 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.091/0.180/0.351 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prove policy enforcement
&lt;/h2&gt;

&lt;p&gt;Requirement three is the one flannel cannot do: NetworkPolicy enforcement. This default-deny policy comes straight from the Kubernetes docs. The empty podSelector selects every pod in the default namespace, and listing both Ingress and Egress with no rules denies all traffic in both directions. Apply it, confirm it exists, and run the exact same ping again. Three packets, zero replies, one hundred percent loss. The policy is not just stored in the API, Calico is enforcing it on the wire. That failing ping is the proof the question asked for.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;deny-all.yaml
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Default-deny-all, straight from the Kubernetes docs: the empty podSelector selects
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;EVERY pod &lt;span class="k"&gt;in &lt;/span&gt;the namespace&lt;span class="p"&gt;;&lt;/span&gt; both policyTypes with no rules &lt;span class="o"&gt;=&lt;/span&gt; deny all traffic.
&lt;span class="go"&gt;apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: default
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deny-all.yaml
&lt;span class="go"&gt;networkpolicy.networking.k8s.io/default-deny-all created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get networkpolicy
&lt;span class="go"&gt;NAME               POD-SELECTOR   AGE
&lt;/span&gt;&lt;span class="gp"&gt;default-deny-all   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;0s
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;test1 &lt;span class="nt"&gt;--&lt;/span&gt; ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 &lt;span class="nt"&gt;-w&lt;/span&gt; 5 &amp;lt;test2-ip&amp;gt;
&lt;span class="go"&gt;PING 192.168.227.198 (192.168.227.198): 56 data bytes

--- 192.168.227.198 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
command terminated with exit code 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps. When a question says the CNI must support NetworkPolicy enforcement, that one phrase eliminates flannel; pick Calico and move on. Use kubectl create for the Calico operator manifests, because apply chokes on the big CRD file. Check that the Installation's IP pool cidr matches the cluster pod CIDR before you create it, then watch kubectl get tigerastatus until everything is Available. And verification means behavior, not status: prove the ping works, then prove a default-deny breaks the same ping.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'Must support NetworkPolicy enforcement' =&amp;gt; flannel is out, use Calico&lt;/li&gt;
&lt;li&gt;kubectl create -f (not apply) for the Calico manifests: the CRD file is too big for apply&lt;/li&gt;
&lt;li&gt;Installation ipPool cidr must match the cluster pod CIDR; watch 'kubectl get tigerastatus'&lt;/li&gt;
&lt;li&gt;Verify behavior: ping works, then a default-deny makes the same ping fail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;NetworkPolicy enforcement required =&amp;gt; Calico, installed with kubectl create (no Helm)&lt;/li&gt;
&lt;li&gt;operator CRDs -&amp;gt; Tigera operator -&amp;gt; Installation (ipPool = cluster pod CIDR)&lt;/li&gt;
&lt;li&gt;tigerastatus Available, nodes Ready, cross-node ping works&lt;/li&gt;
&lt;li&gt;default-deny =&amp;gt; the same ping fails: enforcement proven; subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario9-calico-network-policy
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>networking</category>
      <category>calico</category>
    </item>
    <item>
      <title>Self-Hosting LLMs on Kubernetes: When vLLM Beats Managed APIs on Cost</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Sat, 18 Jul 2026 19:24:13 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/self-hosting-llms-on-kubernetes-when-vllm-beats-managed-apis-on-cost-502k</link>
      <guid>https://dev.to/thecybersidekick/self-hosting-llms-on-kubernetes-when-vllm-beats-managed-apis-on-cost-502k</guid>
      <description>&lt;p&gt;&lt;em&gt;A practitioner's cost-benefit analysis of vLLM on Kubernetes versus OpenAI and other managed inference APIs for high-volume LLM workloads.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Organizations running high-volume LLM inference can reduce per-token costs by 60-80% by self-hosting with vLLM on Kubernetes, but the economics only work after solving GPU scheduling, model serving, and operational complexity. This article walks platform engineers through the breakeven analysis, infrastructure architecture, and operational tooling required to make self-hosted inference viable in production.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Economics: Where Self-Hosted Inference Wins
&lt;/h2&gt;

&lt;p&gt;The LLM inference market is splitting into two camps: managed API providers like OpenAI, Anthropic, and Google Vertex AI charging per-token premiums, and self-hosted inference stacks on Kubernetes that amortize GPU costs across high request volumes. The crossover point sits at roughly 10 to 20 million tokens per day per model, where reserved A100 or H100 instances typically break even against OpenAI API pricing within 3 to 6 months. That window shrinks further when you factor in quantization: AWQ and GPTQ reduce model memory footprint by 2 to 4x, letting you serve more concurrent requests from the same GPU, which directly compresses the breakeven timeline. The economic case is also being accelerated by inference-time compute scaling, where o1-style chain-of-thought reasoning dramatically inflates output token volumes, making per-token API billing increasingly untenable for high-throughput production applications. Open-weight models like Llama 3.1, Mistral, Qwen2, and Gemma 2 have meanwhile closed the capability gap with proprietary APIs for many enterprise use cases, removing the last justification for paying the managed API premium when volume is high enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  vLLM and Kubernetes: The Infrastructure Stack
&lt;/h2&gt;

&lt;p&gt;vLLM, developed by LMSys, implements PagedAttention, a memory management algorithm inspired by OS virtual memory paging that achieves near-zero KV cache memory waste and delivers up to 24x higher throughput than naive HuggingFace Transformers serving. Its continuous batching keeps GPU utilization above 80% under sustained load, compared to static batching which frequently yields sub-40% utilization, and vLLM 0.4 added production-grade features including OpenAI-compatible REST endpoints, speculative decoding, chunked prefill, and multi-LoRA serving that make it a viable drop-in API replacement. On the Kubernetes side, the NVIDIA GPU Operator automates GPU driver installation, device plugin deployment, and MIG partitioning, with H100 MIG allowing a single 80GB GPU to be sliced into up to 7 isolated instances so smaller 7B parameter models can be scheduled on fractional GPU resources alongside larger workloads. For multi-node tensor parallelism, platform teams are choosing between KubeRay with RayServe and native vLLM Kubernetes deployments, with KubeRay offering richer pipeline parallelism and autoscaling primitives while native vLLM deployments reduce operational surface area. Karpenter handles GPU node autoscaling, and spot instance availability for H100 NVLink, AMD MI300X, and Google TPU v5 hardware is continuing to lower amortized cost per token on major cloud providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational Complexity: What You Are Actually Signing Up For
&lt;/h2&gt;

&lt;p&gt;The 60-80% cost reduction is real, but it comes with an operational contract that managed APIs abstract away entirely, and platform teams need to account for that engineering investment before committing. Production vLLM deployments require Prometheus and OpenTelemetry instrumentation at the token level to surface queue depth, time-to-first-token, and inter-token latency metrics, and autoscaling policies need to be built around queue depth rather than the CPU and memory signals that Kubernetes HPA uses by default. Model version management requires Argo Rollouts or equivalent canary tooling to safely promote new model weights or LoRA adapters without dropping traffic, and multi-LoRA hot-swapping for fine-tuned model variants adds another layer of complexity around adapter lifecycle management. Multi-tenant vLLM deployments in internal LLM platforms need namespace isolation, per-team rate limiting, and chargeback instrumentation so that cost savings are actually attributed and not just absorbed into platform overhead. Teams that underestimate this operational complexity often find that the first three months of self-hosting are net-negative on engineering productivity, which is why the 3 to 6 month breakeven estimate should be treated as a floor, not a guarantee, for teams without prior GPU infrastructure experience.&lt;/p&gt;

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

&lt;p&gt;Self-hosted vLLM on Kubernetes is genuinely the right choice for organizations running sustained high-volume LLM inference, but the decision should be driven by a clear-eyed token volume analysis rather than enthusiasm for infrastructure ownership. At 10 to 20 million tokens per day per model, the economics are compelling and the tooling ecosystem around GPU Operator, KubeRay, Karpenter, and Prometheus has matured enough to make production deployments tractable for experienced platform teams. Looking ahead, inference-time compute scaling will continue to inflate token volumes across the industry, which will push more organizations past the breakeven threshold faster than they expect. Hardware commoditization through spot H100 and AMD MI300X availability will further compress managed API margins, making the self-hosted case stronger over the next 12 to 18 months. Platform teams that invest now in building internal LLM inference platforms on Kubernetes, with solid observability, autoscaling, and model lifecycle tooling, will be positioned to serve inference demand at a cost structure that managed APIs structurally cannot match at scale.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; vLLM, Kubernetes, GPU resource management, container orchestration, inference optimization&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: CNCF Blog, Kubernetes.io, DevOps Weekly&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>vllm</category>
      <category>kubernetes</category>
      <category>llminference</category>
      <category>gpuorchestration</category>
    </item>
    <item>
      <title>Platform Engineering for AI-Native Workloads: Managing Cognitive Load at Scale</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Mon, 13 Jul 2026 13:17:53 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/platform-engineering-for-ai-native-workloads-managing-cognitive-load-at-scale-2jnl</link>
      <guid>https://dev.to/thecybersidekick/platform-engineering-for-ai-native-workloads-managing-cognitive-load-at-scale-2jnl</guid>
      <description>&lt;p&gt;&lt;em&gt;How platform teams can architect internal developer platforms optimized for GPU scheduling, model serving, and experiment tracking without overwhelming ML engineers.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI workloads are multiplying exponentially, yet fewer than 30% of organizations have extended their internal developer platforms to natively support GPU workloads and ML pipelines, according to Puppet's 2024 State of Platform Engineering report. Platform teams that close this gap by layering AI-native abstractions atop Kubernetes can dramatically reduce cognitive load for ML engineers while containing runaway GPU costs through intelligent resource orchestration.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Abstraction Gap Undermining AI Platform Maturity
&lt;/h2&gt;

&lt;p&gt;Kubernetes was architected for stateless microservices, and stretching it to accommodate high-memory-bandwidth training jobs, long-running batch workloads, and sub-100ms inference SLAs exposes serious abstraction gaps that raw kubectl access cannot paper over. The result is a tax on ML engineers who must simultaneously master Kubernetes primitives, GPU driver nuances, and distributed training frameworks before writing a single line of model code. Purpose-built control planes including Run:ai, Volcano, and Loft's vCluster are gaining traction precisely because they sit atop Kubernetes and expose ML-specific primitives, such as experiment tracking dashboards, model registries, and GPU quota views, shielding practitioners from infrastructure complexity. The 87% of organizations with mature platform engineering practices that report measurably reduced developer cognitive load have one thing in common: they treat the platform as a product with well-defined, opinionated abstractions rather than as a collection of loosely integrated open-source tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPU Orchestration and Multi-Tenancy Through MIG and KubeRay
&lt;/h2&gt;

&lt;p&gt;Granular GPU resource isolation is now achievable without whole-GPU allocation, and platform teams that ignore this capability are leaving significant efficiency gains on the table. NVIDIA's MIG Manager within the GPU Operator allows a single A100 to be partitioned into up to seven isolated instances, enabling Kubernetes resource quotas as specific as 1g.10gb, which translates to up to 40% less GPU idle time in multi-tenant inference clusters compared to whole-GPU scheduling. For distributed training and online inference, KubeRay has emerged as the most compelling unified compute layer, with adoption growing over 300% year-over-year in 2023 and 2024 based on GitHub stars and Helm chart downloads. Organizations deploying RayService for model inference via KubeRay's operator-based CRDs report sub-100ms p99 latency at scales exceeding 10,000 requests per second through Kubernetes-native horizontal autoscaling, making it a credible alternative to purpose-built inference servers for teams already invested in the Ray ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitOps, Observability, and FinOps as First-Class Platform Concerns
&lt;/h2&gt;

&lt;p&gt;Bringing software engineering discipline to the ML lifecycle requires treating model weights, feature stores, and evaluation datasets with the same versioning rigor applied to application code, and GitOps-driven workflows through ArgoCD and Kubeflow Pipelines v2 with an Argo Workflows backend are making this operationally tractable at scale. Service mesh capabilities via Istio extend this discipline into inference traffic management, enabling weighted routing for shadow deployments and header-based routing for model version targeting, which gives platform teams a safe mechanism for canary model promotions without custom networking code. Observability remains a critical and underinvested area, with leading teams instrumenting ML pipelines through OpenTelemetry, Prometheus custom metrics, and distributed tracing via Tempo to correlate model performance degradation with infrastructure-level anomalies in a single unified trace. On the cost side, GPU spend now dominates cloud bills for AI-heavy organizations, making spot-instance-aware schedulers, idle GPU detection via Prometheus alerting, and per-team chargeback dashboards in Grafana not optional enhancements but core platform features that directly influence engineering budget conversations.&lt;/p&gt;

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

&lt;p&gt;The platform engineering teams that will define the next generation of AI infrastructure are those treating AI-native workloads not as an edge case bolted onto an existing IDP but as the primary design constraint for every abstraction they build. The convergence of MLOps tooling with traditional platform engineering practices is accelerating, and the organizations moving fastest are the ones investing simultaneously in GPU resource isolation through MIG partitioning, unified compute layers like KubeRay, GitOps-native model promotion pipelines, and FinOps visibility that holds teams accountable for GPU utilization. As foundation model sizes grow and inference latency budgets tighten, the pressure on platform teams to deliver self-service ML infrastructure without cognitive overload will only intensify, making purpose-built AI platform abstractions one of the highest-leverage bets an engineering organization can make in the next 18 months.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; Kubernetes GPU scheduling and resource quotas, Ray and Kubeflow for distributed ML, Service mesh (Istio) for model inference routing, ArgoCD for MLOps GitOps, Observability stacks (Prometheus, Grafana, Tempo) for ML pipeline tracing, Containerization and OCI standards&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: CNCF Blog, Kubernetes.io, DevOps Weekly&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>platformengineering</category>
      <category>kubernetes</category>
      <category>gpuorchestration</category>
      <category>mlops</category>
    </item>
    <item>
      <title>GhostLock (CVE-2026-43499): How a Linux Kernel Privilege Escalation Exposes Kubernetes Multi-Tenant Security Gaps</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Mon, 13 Jul 2026 12:44:15 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/ghostlock-cve-2026-43499-how-a-linux-kernel-privilege-escalation-exposes-kubernetes-multi-tenant-4703</link>
      <guid>https://dev.to/thecybersidekick/ghostlock-cve-2026-43499-how-a-linux-kernel-privilege-escalation-exposes-kubernetes-multi-tenant-4703</guid>
      <description>&lt;p&gt;&lt;em&gt;A newly disclosed kernel vulnerability forces a hard reassessment of the container isolation assumptions underpinning multi-tenant Kubernetes clusters.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CVE-2026-43499, dubbed GhostLock, is a Linux kernel privilege escalation vulnerability that allows unprivileged processes to gain root-level access by exploiting flaws in kernel subsystems, effectively collapsing the security boundary between a container and its host node. Kubernetes operators running multi-tenant clusters are acutely exposed because Kubernetes-native controls like Pod Security Admission, seccomp profiles, and AppArmor all operate above the kernel layer and cannot compensate for an unpatched host kernel.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Kernel Privilege Escalation Hits Kubernetes Differently
&lt;/h2&gt;

&lt;p&gt;Kubernetes delegates container isolation to Linux kernel primitives: namespaces partition process visibility, cgroups enforce resource boundaries, and capabilities constrain privileged operations. When a vulnerability like GhostLock allows an attacker inside a container to escalate to root on the host kernel, every one of those primitives becomes irrelevant. In a multi-tenant cluster where a single node may run hundreds of pods across different trust boundaries, a single exploitable container can become a foothold into the entire node and, from there, into cluster control plane credentials mounted via service account tokens. The Linux kernel averaged between 1,800 and 2,000 CVEs per year from 2020 to 2023 according to NVD data, with privilege escalation categories consistently representing the highest-severity subset, yet enterprise cluster upgrade cycles routinely lag behind kernel patch cadences, leaving nodes exposed for weeks or months at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Kubernetes Hardening Falls Short Against Kernel CVEs
&lt;/h2&gt;

&lt;p&gt;The deprecation of PodSecurityPolicy in Kubernetes 1.25 and its replacement with Pod Security Admission has left many teams with coarser enforcement granularity, particularly around syscall restrictions. According to the 2023 CNCF Security Report, over 60 percent of production Kubernetes clusters surveyed did not enforce seccomp profiles by default, meaning the syscall-level attack surface that GhostLock targets is fully exposed in the majority of real-world deployments. Aqua Security research further identified that container escape techniques leveraging host kernel vulnerabilities represent a significant portion of realistic attack paths, with privileged pods and hostPath mounts acting as common amplifying misconfigurations that lower the bar for exploitation. Seccomp, surfaced through Kubernetes securityContext, can reduce the exploitable syscall surface, but it requires deliberate, per-workload policy authoring that most teams have not yet operationalized at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detection, Mitigation, and Stronger Isolation Primitives
&lt;/h2&gt;

&lt;p&gt;Practitioners responding to GhostLock have three complementary mitigation layers available today. First, prioritize emergency node patching or, better, replace nodes entirely using immutable image-based operating systems like Bottlerocket or Flatcar Linux, where full node replacement is faster and more automated than in-place patching, directly closing the kernel patch lag window. Second, deploy eBPF-based runtime security tooling such as the CNCF Falco project, which instruments the kernel to detect anomalous syscall patterns consistent with privilege escalation attempts, providing detection coverage while patching cycles complete. Third, for workloads with the highest trust sensitivity, adopt syscall interposition sandboxes like gVisor, which interposes on syscalls through a user-space kernel, dramatically reducing the exposed host kernel attack surface and making kernel CVEs like GhostLock largely irrelevant to sandboxed workloads. Confidential computing approaches using AMD SEV or Intel TDX provide hardware-enforced memory isolation that can further constrain what a kernel-level attacker can observe or modify across tenant boundaries.&lt;/p&gt;

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

&lt;p&gt;GhostLock is not an anomaly; it is a predictable entry in a long series of kernel privilege escalation vulnerabilities that will continue to challenge the container isolation model Kubernetes depends on. The fundamental tension is that Kubernetes security controls are policy abstractions layered on top of a shared kernel, and no amount of policy sophistication fully compensates for an unpatched vulnerability in that shared kernel. The industry trajectory toward immutable node infrastructure, eBPF-based runtime observability, and hardware-enforced isolation through confidential computing represents the correct long-term response, moving isolation guarantees progressively closer to hardware and further from software policies that can be bypassed. In the near term, operators should treat kernel CVE patching with the same urgency as control plane upgrades, enforce seccomp profiles broadly using the RuntimeDefault baseline as a starting point, and audit clusters for privileged pods and hostPath mounts that would amplify any successful kernel exploit into a full cluster compromise.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; Linux kernel, Kubernetes, container security, privilege escalation, pod security policies, seccomp&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: CNCF Blog, Kubernetes.io, DevOps Weekly, Hacker News, InfoQ&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>cve202643499</category>
      <category>linuxkernelsecurity</category>
      <category>kubernetesprivilegeescalation</category>
      <category>containersecurity</category>
    </item>
    <item>
      <title>2026 CKA Exam - Scenario 8 Install a CNI and fix the flannel pod-CIDR mismatch (CKA Services &amp; Networking)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Fri, 10 Jul 2026 13:02:27 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/install-a-cni-and-fix-the-flannel-pod-cidr-mismatch-cka-services-networking-5hf4</link>
      <guid>https://dev.to/thecybersidekick/install-a-cni-and-fix-the-flannel-pod-cidr-mismatch-cka-services-networking-5hf4</guid>
      <description>&lt;h2&gt;
  
  
  Install a CNI and fix the pod-CIDR mismatch
&lt;/h2&gt;

&lt;p&gt;The cluster is running, but every node is NotReady, because no network plugin is installed. The exam gives you a flannel manifest and asks you to install and configure a CNI. Let's apply it, watch it fail, find out why, and fix it.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=wzFE66kfRl4" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=wzFE66kfRl4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Services &amp;amp; Networking walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;Here is the setup. A fresh cluster has no CNI, so its nodes are NotReady and CoreDNS is stuck Pending. You are handed a flannel manifest and told to install a network plugin of your choice. The catch, which you only discover after applying, is that the manifest's network does not match the cluster's pod CIDR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fresh cluster with NO CNI: nodes NotReady, CoreDNS Pending&lt;/li&gt;
&lt;li&gt;You're handed a flannel manifest and told to install a CNI&lt;/li&gt;
&lt;li&gt;The manifest's Network (10.244.0.0/16) != the cluster pod CIDR (192.168.0.0/16)&lt;/li&gt;
&lt;li&gt;Install it, fix the mismatch, and prove pods can talk&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How flannel and CNIs work
&lt;/h2&gt;

&lt;p&gt;A CNI plugin is what gives pods IP addresses and wires up pod-to-pod routing; without one the kubelet reports the node NotReady. flannel runs as a DaemonSet, one pod per node, and reads its settings from a ConfigMap called kube-flannel-cfg. The important field is net-conf.json Network, the address space flannel hands out. Because flannel runs with kube-subnet-mgr, it also reads each node's assigned podCIDR, and it refuses to start if that podCIDR is not inside its configured Network. Match those two and it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  No CNI: nodes NotReady
&lt;/h2&gt;

&lt;p&gt;Start by confirming the starting state. Every node is NotReady, the classic symptom of a missing CNI. And in kube-system, CoreDNS is Pending because it has no pod network to join yet. Everything else the control plane needs is up, so this really is just the network plugin that's missing.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;span class="go"&gt;NAME                          STATUS     ROLES           AGE   VERSION
cka-scenario8-control-plane   NotReady   control-plane   8d    v1.36.1
&lt;/span&gt;&lt;span class="gp"&gt;cka-scenario8-worker          NotReady   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;8d    v1.36.1
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="go"&gt;NAME                                                  READY   STATUS    RESTARTS   AGE   IP            NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;coredns-589f44dc88-8m6kd                              1/1     Running   0          8d    192.168.1.4   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;coredns-589f44dc88-shgqb                              1/1     Running   0          8d    192.168.1.3   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;etcd-cka-scenario8-control-plane                      1/1     Running   0          8d    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-apiserver-cka-scenario8-control-plane            1/1     Running   0          8d    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-controller-manager-cka-scenario8-control-plane   1/1     Running   0          8d    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-proxy-gkw9p                                      1/1     Running   0          8d    172.18.0.14   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-proxy-nxbwr                                      1/1     Running   0          8d    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-scheduler-cka-scenario8-control-plane            1/1     Running   0          8d    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Apply flannel (it CrashLoops)
&lt;/h2&gt;

&lt;p&gt;Now install flannel by applying the manifest. It creates the kube-flannel namespace, the ConfigMap, and the DaemonSet. But when you look at the pods a few seconds later, they are not Running: they're in CrashLoopBackOff or Error. Applying the manifest was necessary, but on this cluster it is not sufficient.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; kube-flannel.yml
&lt;span class="go"&gt;namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel unchanged
clusterrolebinding.rbac.authorization.k8s.io/flannel unchanged
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-flannel get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="go"&gt;NAME                    READY   STATUS   RESTARTS     AGE   IP            NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;kube-flannel-ds-6mhjm   0/1     Error    1 (9s ago)   12s   172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-flannel-ds-jq4n7   0/1     Error    1 (9s ago)   12s   172.18.0.14   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read the lease error
&lt;/h2&gt;

&lt;p&gt;Don't guess, read the logs. The flannel pod says it failed to acquire a lease, because the node's pod subnet, a slice of 192.168.0.0/16, is not inside the flannel net configuration of 10.244.0.0/16. That's the whole problem in one line: the manifest ships a default Network that does not match how this cluster was built. The cluster's pod CIDR is authoritative, so flannel is the thing that has to change.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-flannel logs &amp;lt;flannel-pod&amp;gt;
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;I0710 11:54:49.724901       1 kube.go:163] Node controller sync successful
I0710 11:54:49.724926       1 main.go:252] Created subnet manager: Kubernetes Subnet Manager - cka-scenario8-control-plane
I0710 11:54:49.724929       1 main.go:255] Installing signal handlers
I0710 11:54:49.725064       1 main.go:534] Found network config - Backend type: vxlan
I0710 11:54:49.726846       1 kube.go:737] List of node(cka-scenario8-control-plane) annotations: map[string]string{"flannel.alpha.coreos.com/backend-data":"{\"VNI\":1,\"VtepMAC\":\"76:f3:d3:22:ef:55\"}", "flannel.alpha.coreos.com/backend-type":"vxlan", "flannel.alpha.coreos.com/kube-subnet-manager":"true", "flannel.alpha.coreos.com/public-ip":"172.18.0.13", "node.alpha.kubernetes.io/ttl":"0", "volumes.kubernetes.io/controller-managed-attach-detach":"true"}
I0710 11:54:49.726881       1 match.go:211] Determining IP address of default interface
I0710 11:54:49.727126       1 match.go:269] Using interface with name eth0 and address 172.18.0.13
I0710 11:54:49.727151       1 match.go:291] Defaulting external address to interface address (172.18.0.13)
I0710 11:54:49.727200       1 vxlan.go:128] VXLAN config: VNI=1 Port=0 GBP=false Learning=false DirectRouting=false
I0710 11:54:49.728578       1 kube.go:704] List of node(cka-scenario8-control-plane) annotations: map[string]string{"flannel.alpha.coreos.com/backend-data":"{\"VNI\":1,\"VtepMAC\":\"76:f3:d3:22:ef:55\"}", "flannel.alpha.coreos.com/backend-type":"vxlan", "flannel.alpha.coreos.com/kube-subnet-manager":"true", "flannel.alpha.coreos.com/public-ip":"172.18.0.13", "node.alpha.kubernetes.io/ttl":"0", "volumes.kubernetes.io/controller-managed-attach-detach":"true"}
I0710 11:54:49.728602       1 vxlan.go:199] Interface flannel.1 mac address set to: 76:f3:d3:22:ef:55
E0710 11:54:49.729096       1 main.go:381] Error registering network: failed to acquire lease: subnet "10.244.0.0/16" specified in the flannel net config doesn't contain "192.168.0.0/24" PodCIDR of the "cka-scenario8-control-plane" node
I0710 11:54:49.729142       1 main.go:514] Stopping shutdownHandler...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fix the CIDR in the ConfigMap
&lt;/h2&gt;

&lt;p&gt;Fix it in the ConfigMap. kubectl edit opens the live kube-flannel-cfg object in vi. Inside net-conf.json, Network is set to 10.244.0.0/16, the manifest's default. Change it to 192.168.0.0/16 so it matches the cluster's pod CIDR, then save and quit. kubectl pushes the edit back to the API server the moment you write the file. Nothing else in the ConfigMap changes.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-flannel edit configmap kube-flannel-cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "Network": "10.244.0.0/16",
&lt;/span&gt;&lt;span class="gi"&gt;+ "Network": "192.168.0.0/16",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restart, then nodes go Ready
&lt;/h2&gt;

&lt;p&gt;A ConfigMap change does not restart pods on its own, so the flannel pods keep crashing on the old config until you cycle them. Delete the flannel pods and the DaemonSet recreates them, this time reading the corrected Network. Now they come up Running, and within a few seconds the nodes flip to Ready and CoreDNS schedules. The CNI is installed and configured.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-flannel delete pod &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flannel
&lt;span class="go"&gt;pod "kube-flannel-ds-6mhjm" deleted from kube-flannel namespace
pod "kube-flannel-ds-jq4n7" deleted from kube-flannel namespace

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-flannel get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;span class="go"&gt;NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;kube-flannel-ds-5c2pb   1/1     Running   0          3s    172.18.0.13   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;kube-flannel-ds-fntnk   1/1     Running   0          3s    172.18.0.14   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get nodes
&lt;span class="go"&gt;NAME                          STATUS   ROLES           AGE   VERSION
cka-scenario8-control-plane   Ready    control-plane   8d    v1.36.1
&lt;/span&gt;&lt;span class="gp"&gt;cka-scenario8-worker          Ready    &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;8d    v1.36.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prove pod-to-pod connectivity
&lt;/h2&gt;

&lt;p&gt;Finish by proving it actually networks, not just that the pods are green. Create two pods on different nodes and list them wide: each has an IP from 192.168.0.0/16, on a different host. Ping one from the other and the packets ride the vxlan overlay between nodes. Replies come back, so the CNI is doing its job end to end.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; connectivity-test.yaml
&lt;span class="go"&gt;pod/test1 created
pod/test2 created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;conn-test
&lt;span class="go"&gt;NAME    READY   STATUS    RESTARTS   AGE   IP            NODE                          NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class="gp"&gt;test1   1/1     Running   0          1s    192.168.1.8   cka-scenario8-worker          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="gp"&gt;test2   1/1     Running   0          1s    192.168.0.5   cka-scenario8-control-plane   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nb"&gt;exec &lt;/span&gt;test1 &lt;span class="nt"&gt;--&lt;/span&gt; ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 &amp;lt;test2-ip&amp;gt;
&lt;span class="go"&gt;PING 192.168.0.5 (192.168.0.5): 56 data bytes
64 bytes from 192.168.0.5: seq=0 ttl=62 time=0.507 ms
64 bytes from 192.168.0.5: seq=1 ttl=62 time=0.096 ms
64 bytes from 192.168.0.5: seq=2 ttl=62 time=0.151 ms

--- 192.168.0.5 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.096/0.251/0.507 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps. NotReady nodes plus Pending CoreDNS almost always means no CNI, so install one first. After you apply a CNI, always check its pods are actually Running, don't assume the apply worked. When flannel logs failed to acquire a lease, that is a pod CIDR versus Network mismatch: make flannel's Network match the cluster, since the cluster CIDR is fixed. And remember a ConfigMap edit needs a pod restart to take effect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NotReady nodes + Pending CoreDNS =&amp;gt; no CNI installed&lt;/li&gt;
&lt;li&gt;After applying a CNI, verify its pods are Running (don't assume)&lt;/li&gt;
&lt;li&gt;'failed to acquire lease' = flannel Network must match the cluster pod CIDR&lt;/li&gt;
&lt;li&gt;A ConfigMap edit needs a pod restart to take effect&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No CNI =&amp;gt; NotReady; install flannel from the manifest&lt;/li&gt;
&lt;li&gt;CrashLoop =&amp;gt; logs =&amp;gt; flannel Network must match the cluster pod CIDR&lt;/li&gt;
&lt;li&gt;Fix net-conf.json, restart the pods, nodes go Ready&lt;/li&gt;
&lt;li&gt;Prove it with a cross-node ping; subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario8-cni-flannel-install
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>networking</category>
      <category>cni</category>
    </item>
    <item>
      <title>AI Agents in DevOps: Why Traditional CI/CD Pipelines Break at 1000 Deployments Per Month</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Wed, 08 Jul 2026 19:03:00 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/ai-agents-in-devops-why-traditional-cicd-pipelines-break-at-1000-deployments-per-month-eoh</link>
      <guid>https://dev.to/thecybersidekick/ai-agents-in-devops-why-traditional-cicd-pipelines-break-at-1000-deployments-per-month-eoh</guid>
      <description>&lt;p&gt;&lt;em&gt;How LLM-driven orchestration agents are replacing rule-based pipeline automation to sustain hyperscale deployment velocities that static DSLs were never designed to handle.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At hyperscale deployment velocities, traditional CI/CD pipelines built on sequential, rule-based automation collapse under the cognitive load of thousands of concurrent deployment decisions that require real-time reasoning across telemetry signals, failure modes, and risk tolerances. A new class of autonomous deployment agents, combining LLM-based orchestration, GitOps declarative state management, and eBPF-powered observability, is emerging as the only viable architecture for platforms that must ship reliably at this scale.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Scaling Wall That Rules-Based Pipelines Cannot Climb
&lt;/h2&gt;

&lt;p&gt;Traditional CI/CD pipelines were architected for deployment velocities measured in dozens of releases per day, using sequential stage gates, hardcoded approval thresholds, and static rollback conditions encoded in Jenkinsfiles or GitHub Actions YAML. At 1000 deployments per month across heterogeneous Kubernetes clusters, these pipelines do not simply slow down; they produce compounding decision debt, where a misconfigured canary threshold written six months ago now governs a microservice that serves ten times the original traffic volume. Google's internal Borg-derived systems already handle over 4 billion container launches per week, a scale that makes the limitations of rule-based scheduling immediately visible, since no human-authored ruleset can evaluate scheduling and deployment constraints within the sub-second latency budgets those systems require. The fundamental architectural mismatch is not one of tooling performance but of decision architecture: static pipelines can execute instructions, but they cannot reason about novel failure combinations, predict cascading degradations across service meshes, or rewrite their own deployment strategies in response to real-time SLO signals.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Agentic Orchestration Layers Replace Static Pipeline DSLs
&lt;/h2&gt;

&lt;p&gt;The ecosystem is actively transitioning from imperative pipeline scripting toward agentic orchestration layers where LLMs serve as meta-controllers, dynamically composing deployment strategies by consuming Prometheus metrics, distributed traces, and changelog semantics simultaneously. Projects like Argo Rollouts are embedding AI-augmented analysis templates that ingest Datadog and Prometheus metric providers to make autonomous canary promotion decisions, eliminating the manual threshold tuning that becomes untenable across hundreds of services. Fluxcd paired with OpenAI function-calling agents enables intelligent drift detection and self-correcting GitOps reconciliation loops, where the agent can distinguish between an intentional declarative state change and an unauthorized configuration drift without requiring a human to inspect the diff. Keptn v2 Lifecycle Toolkit extends this further by providing OpenTelemetry-native evaluation hooks that AI agents consume for SLO-driven deployment gating, meaning a deployment can be autonomously promoted, paused, or rolled back based on a structured conversation between the orchestration agent and a unified observability substrate rather than a brittle shell script comparing integer thresholds. Platforms like Dagger are enabling portable, composable pipeline primitives that LLM agents can assemble on-demand, shifting engineering teams from maintaining pipeline code to expressing desired deployment outcomes and acceptable risk tolerances as declarative intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Observability and Infrastructure Substrate That Makes Agents Viable
&lt;/h2&gt;

&lt;p&gt;Autonomous deployment agents require a standardized signal vocabulary to reason reliably, and the maturation of OpenTelemetry as a universal observability substrate across traces, metrics, and logs is providing exactly that foundation at a moment when it is most needed. Without a consistent schema for telemetry signals, an LLM-based agent cannot reliably distinguish a latency spike caused by a flawed deployment from one caused by an upstream dependency degrading independently, making autonomous rollback decisions dangerous rather than helpful. The infrastructure layer is also evolving to meet agents where they need to operate: Kubernetes Gateway API and WASM-based extensibility now allow AI agents to manipulate traffic routing at a granularity that previously required manual SRE intervention, enabling progressive delivery patterns like weighted traffic splits and header-based routing to be adjusted dynamically as canary analysis proceeds. Kubernetes-native admission webhooks and CEL-based policy surfaces give agents a programmable enforcement plane they can update at runtime without requiring cluster restarts or human-authored policy changes. Datadog's 2024 Container Report quantifies what happens when this infrastructure is absent, finding that organizations running more than 500 Kubernetes nodes experience incident rates 3.2 times higher during deployment windows, with the average cost per major outage reaching approximately $2.3 million, a figure that makes the ROI case for AI-driven progressive delivery and automated rollback straightforward to calculate.&lt;/p&gt;

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

&lt;p&gt;The 2023 DORA State of DevOps Report found that elite performers deploy 182 times more frequently than low performers, and analysts project that AI-assisted pipelines will push that multiplier beyond 500 times by 2026 as autonomous deployment agents eliminate manual approval bottlenecks and replace them with SLO-aware, telemetry-driven decision loops. The path forward is not incrementally smarter pipeline scripts but a wholesale architectural shift toward declarative intent expression, where engineering teams define outcomes and risk tolerances while agents handle tactical execution across multi-cluster federation topologies, availability zone-aware scheduling, and real-time traffic shaping. Organizations that begin this transition now, starting with AI-augmented canary analysis on top of existing Argo Rollouts or Flux installations, will build the operational muscle memory and telemetry hygiene needed to run fully autonomous deployment systems before the next generation of deployment velocity expectations arrives. Those that wait for the tools to mature further may find that the velocity gap between elite and average performers has grown too wide to close through iteration alone.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; AI agents (LLM-based orchestration), GitOps with intelligent rollback, Kubernetes native auto-scaling, Observability platforms (Datadog, Prometheus), Self-healing infrastructure&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: DevOps Weekly, GitHub Trending, Hacker News, InfoQ&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>aiagents</category>
      <category>devopsautomation</category>
      <category>cicdpipelines</category>
      <category>gitops</category>
    </item>
    <item>
      <title>GitOps at Scale: How Event-Driven and AI-Assisted Deployments Are Replacing Manual Environment Promotion</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Thu, 02 Jul 2026 19:51:44 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/gitops-at-scale-how-event-driven-and-ai-assisted-deployments-are-replacing-manual-environment-4lj5</link>
      <guid>https://dev.to/thecybersidekick/gitops-at-scale-how-event-driven-and-ai-assisted-deployments-are-replacing-manual-environment-4lj5</guid>
      <description>&lt;p&gt;&lt;em&gt;Event-driven architectures and AI-powered validation are automating the entire GitOps promotion pipeline, eliminating the manual bottlenecks that throttle release velocity in large-scale Kubernetes environments.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Organizations managing hundreds of microservices are discovering that traditional GitOps promotion workflows, built around manual approval gates and human intervention, cannot scale to meet the demands of modern cloud-native delivery. Event-driven automation combined with ML-based quality gates is now enabling fully autonomous promotion decisions driven by real-time observability signals, policy-as-code enforcement, and historical deployment telemetry.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Scaling Ceiling of Manual GitOps Promotion
&lt;/h2&gt;

&lt;p&gt;Traditional GitOps pipelines treat environment promotion as a human-coordinated handoff: an engineer reviews test results, eyeballs dashboards, and clicks an approval button to advance a workload from staging to production. This model collapses under the weight of scale. When a platform team is responsible for hundreds of microservices across dozens of clusters, manual gates become the rate-limiting step in every release cycle. According to the 2023 DORA State of DevOps Report, elite performers deploy 127 times more frequently than low performers, and automated promotion pipelines are consistently cited as a key differentiator that keeps change failure rates below five percent. The problem is not that engineers make poor decisions; it is that the volume of decisions required in a large-scale Kubernetes environment exceeds what any team can handle reliably and quickly without automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Driven Promotion: Wiring Observability Signals Into the GitOps Control Loop
&lt;/h2&gt;

&lt;p&gt;The practical solution emerging across the CNCF ecosystem is to replace human approval gates with event-driven promotion logic that consumes signals from the observability stack in real time. Progressive delivery controllers like Argo Rollouts and Flagger connect directly to Prometheus, Datadog, and OpenTelemetry data sources, using metric-driven analysis templates to make canary and blue-green promotion decisions without waiting for a human to read a dashboard. Platform teams are routing Kubernetes events through NATS, Kafka, and CloudEvents-compliant brokers into GitOps reconcilers, so that SLO breaches, security scan failures, and load test outcomes can automatically trigger or block ArgoCD ApplicationSet promotions the moment the signal is available. Argo Rollouts alone has accumulated more than 5,800 GitHub stars and is running in production environments managing thousands of workloads, with documented case studies reporting a 60 to 70 percent reduction in deployment incidents attributable to analysis-based automated promotion. The CNCF's convergence on CloudEvents as a universal eventing substrate is accelerating interoperability between Tekton, Argo Events, Keptn, and external vendors, making it increasingly practical to compose these signals into a single, coherent promotion control plane.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI-Augmented Quality Gates and Policy-as-Code Guardrails
&lt;/h2&gt;

&lt;p&gt;Event-driven promotion handles the mechanics of signal routing, but AI and ML layers are adding a higher-order capability: deployment risk scoring based on patterns in historical telemetry that no human analyst would have the bandwidth to synthesize in real time. Tools like Keptn are integrating ML models trained on past deployment outcomes to score incoming releases and automate rollback decisions before a bad deployment can propagate to production. OpenFeature and custom admission webhooks are emerging as integration points for embedding these models directly into the Kubernetes API surface, while the Flux CD Notification Controller extends GitOps reconciliation triggers to respond to external quality signals via CloudEvents. Alongside AI scoring, policy-as-code frameworks are shifting compliance enforcement left: OPA Gatekeeper and Kyverno are now validating promotion eligibility before a Git commit is even merged, not just at deployment time, creating a continuous compliance loop across the entire software development lifecycle. Gartner projects that by 2026, more than 60 percent of organizations with mature DevOps practices will implement AI-augmented continuous delivery pipelines, up from fewer than 10 percent in 2023, driven by the economics of reducing mean time to recovery in cloud-native environments.&lt;/p&gt;

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

&lt;p&gt;The convergence of event-driven architecture, progressive delivery controllers, and AI-augmented quality gates is fundamentally reshaping what a GitOps promotion pipeline looks like at scale. Platform engineering teams are already standardizing on Internal Developer Platforms that abstract promotion complexity behind golden paths, embedding these capabilities directly into Backstage templates and Crossplane compositions so that individual service teams inherit automated, policy-compliant promotion by default rather than by custom effort. The trajectory is clear: the approval button is being replaced by a scoring model, the Slack notification is being replaced by a CloudEvent, and the manual rollback is being replaced by an analysis-driven controller acting within seconds of a signal breach. Organizations that invest now in the observability instrumentation, eventing infrastructure, and policy-as-code discipline required to feed these systems will be positioned to treat safe, frequent, autonomous deployment not as an aspirational benchmark but as a daily operational baseline.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; GitOps, Event-Driven Architecture, Kubernetes, CI/CD Pipelines, Machine Learning Operations, ArgoCD, Flux CD, Policy as Code&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: CNCF Blog, Kubernetes.io, DevOps Weekly&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>gitops</category>
      <category>eventdrivenarchitecture</category>
      <category>kubernetes</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Autoscale a Deployment with an HPA, including the field kubectl autoscale can't set (CKA Workloads)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Wed, 01 Jul 2026 14:56:10 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/autoscale-a-deployment-with-an-hpa-including-the-field-kubectl-autoscale-cant-set-cka-workloads-2gn0</link>
      <guid>https://dev.to/thecybersidekick/autoscale-a-deployment-with-an-hpa-including-the-field-kubectl-autoscale-cant-set-cka-workloads-2gn0</guid>
      <description>&lt;h2&gt;
  
  
  Autoscale a Deployment with an HPA
&lt;/h2&gt;

&lt;p&gt;A Deployment named apache-server is running, and the exam wants it autoscaled on CPU: a 50 percent target, between one and four pods, and a 30 second scaleDown stabilization window. Let's build the HorizontalPodAutoscaler, and see why the obvious one-line command can't finish it.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=G4ZFpOXMJD8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=G4ZFpOXMJD8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Workloads &amp;amp; Scheduling walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;Here is the task. In the autoscale namespace, create a HorizontalPodAutoscaler named apache-server that targets the existing apache-server Deployment. Set the CPU target to 50 percent average utilization per pod, allow a minimum of one pod and a maximum of four, and set the scaleDown stabilization window to 30 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Namespace autoscale, HPA named apache-server&lt;/li&gt;
&lt;li&gt;Target the existing apache-server Deployment&lt;/li&gt;
&lt;li&gt;CPU target: 50% average utilization per pod&lt;/li&gt;
&lt;li&gt;minReplicas 1, maxReplicas 4, scaleDown window 30s&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How a HorizontalPodAutoscaler works
&lt;/h2&gt;

&lt;p&gt;Two things have to be true before an HPA can do anything. The Pod needs a CPU request, because a 50 percent target is a percentage of that request; with no request the HPA can't compute utilization. And metrics-server has to be running, because that's where the HPA reads live CPU. Given both, the HPA watches CPU and adjusts replicas between your min and max. The scaleDown stabilization window tells it how long to wait on falling load before removing pods, which damps flapping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the YAML comes from (official docs)
&lt;/h2&gt;

&lt;p&gt;Before you apply anything, know that there is no kubectl create that emits a behavior block, so part of this is copy-paste from the official docs, which you are allowed to use in the exam. Two pages on kubernetes dot io cover it. Search HPA and open the HorizontalPodAutoscaler Walkthrough; it shows the autoscaling v2 object with the metrics array, the shape for the CPU target. Then open the Horizontal Pod Autoscaling concept page and find the section called Configurable scaling behavior, with its Stabilization window example; lift the scaleDown block straight from there. In practice: generate the skeleton with kubectl autoscale dry-run, paste the behavior block from that section, and set the window to 30.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HPA object: &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;behavior block: &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Start by reading what's there. The apache-server Deployment and its Service are running in the autoscale namespace. Open the Deployment manifest and look at the container's resources. Crucially, the Pod template sets a CPU request of 200 millicores. That request is what makes a percentage target meaningful: 50 percent of 200 millicores is 100 millicores per pod.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale get deploy,svc
&lt;span class="go"&gt;NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/apache-server   1/1     1            1           125m

NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
&lt;/span&gt;&lt;span class="gp"&gt;service/apache-server   ClusterIP   10.96.182.94   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;80/TCP    125m
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;apache-app.yaml
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;      labels:
        app: apache-server
    spec:
      containers:
        - name: php-apache
          image: registry.k8s.io/hpa-example
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 200m
            limits:
              cpu: 500m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Metrics are flowing
&lt;/h2&gt;

&lt;p&gt;Confirm the HPA will have data to read. kubectl top pods returns live CPU and memory from metrics-server. If this command errored or showed nothing, the HPA would sit at TARGETS unknown and never scale, so verifying metrics first saves you a confusing debug later.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale top pods
&lt;span class="go"&gt;NAME                             CPU(cores)   MEMORY(bytes)   
apache-server-748dd94f84-2h56f   1m           9Mi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where kubectl autoscale stops
&lt;/h2&gt;

&lt;p&gt;Now the fast path: kubectl autoscale. Pass the target, min, and max, and it builds an HPA in one line. On a current cluster the dry-run even emits apiVersion autoscaling slash v2, with the CPU target already inside a metrics array, so it looks like you're done. But scan the whole object: there is no behavior section anywhere. The scaleDown stabilization window has no flag on this command, so the imperative path gets you three of the four requirements and stops.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale autoscale deployment apache-server &lt;span class="nt"&gt;--cpu-percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50 &lt;span class="nt"&gt;--min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;--max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &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="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;      name: cpu
      target:
        averageUtilization: 50
        type: Utilization
    type: Resource
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: apache-server
status:
  currentMetrics: null
  desiredReplicas: 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Author the v2 HPA
&lt;/h2&gt;

&lt;p&gt;So finish it in YAML. Take the skeleton and add the one piece the command can't give you: a behavior block where scaleDown stabilizationWindowSeconds is 30. Watch the trap the walkthrough hit: in autoscaling slash v2 the CPU target belongs inside the metrics array as averageUtilization, so don't paste an old v1 targetCPUUtilizationPercentage field next to behavior, or the apiserver rejects it as an unknown field. Apply it, then list the HPA: it shows the target against 50 percent, with min 1 and max 4.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; hpa.yaml
&lt;span class="go"&gt;horizontalpodautoscaler.autoscaling/apache-server created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale get hpa apache-server
&lt;span class="go"&gt;NAME            REFERENCE                  TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
apache-server   Deployment/apache-server   cpu: 0%/50%   1         4         1          5s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify the HPA
&lt;/h2&gt;

&lt;p&gt;Verify every field, because each is a separate mark. describe confirms the reference to the Deployment, the 50 percent CPU target, and the one-to-four range. Then read the stabilization window straight from the spec: 30 seconds, exactly as asked. With the CPU target reading a real percentage, the autoscaler is live and complete.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale describe hpa apache-server
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;  Scale Down:
    Stabilization Window: 30 seconds
    Select Policy: Max
    Policies:
      - Type: Percent  Value: 100  Period: 15 seconds
Deployment pods:       1 current / 1 desired
Conditions:
  Type            Status  Reason            Message
  ----            ------  ------            -------
  AbleToScale     True    ReadyForNewScale  recommended size matches current size
  ScalingActive   True    ValidMetricFound  the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)
  ScalingLimited  True    TooFewReplicas    the desired replica count is less than the minimum replica count
&lt;/span&gt;&lt;span class="gp"&gt;Events:           &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; autoscale get hpa apache-server &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.spec.behavior.scaleDown.stabilizationWindowSeconds}'&lt;/span&gt;
&lt;span class="go"&gt;scaleDown stabilizationWindowSeconds = 30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps to remember. kubectl autoscale is fine for min, max, and the CPU target, but it has no flag for a stabilization window, so when behavior is required you finish the HPA in YAML. In autoscaling v2 the CPU target lives inside the metrics array, not as targetCPUUtilizationPercentage; mixing the two is the unknown field error. The Pod needs a CPU request or the percentage target is meaningless. And if TARGETS shows unknown, suspect metrics-server before the HPA itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;kubectl autoscale has no flag for a scaleDown stabilization window; add it in YAML&lt;/li&gt;
&lt;li&gt;v2: CPU target goes in metrics[], not targetCPUUtilizationPercentage&lt;/li&gt;
&lt;li&gt;No CPU request on the Pod means no usable utilization target&lt;/li&gt;
&lt;li&gt;TARGETS ? Check metrics-server before blaming the HPA&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prereqs: a CPU request on the Pod + a running metrics-server&lt;/li&gt;
&lt;li&gt;v2 manifest: metrics[] for the CPU target, behavior for scaleDown&lt;/li&gt;
&lt;li&gt;stabilizationWindowSeconds 30 is the field kubectl autoscale can't set&lt;/li&gt;
&lt;li&gt;Subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario7-hpa-cpu-autoscale
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>autoscaling</category>
      <category>workloads</category>
    </item>
    <item>
      <title>CKA Exam 2026 - Scenario 6 Migrate an Ingress to the Gateway API without dropping HTTPS (CKA Services &amp; Networking)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Mon, 29 Jun 2026 18:29:18 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/migrate-an-ingress-to-the-gateway-api-without-dropping-https-cka-services-networking-549l</link>
      <guid>https://dev.to/thecybersidekick/migrate-an-ingress-to-the-gateway-api-without-dropping-https-cka-services-networking-549l</guid>
      <description>&lt;h2&gt;
  
  
  Migrate Ingress to the Gateway API
&lt;/h2&gt;

&lt;p&gt;A web application is exposed over HTTPS with a classic Ingress, and the exam wants it migrated to the Gateway API while keeping HTTPS working. The GatewayClass is already installed. Let's recreate the routing with a Gateway and an HTTPRoute, then retire the Ingress.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=v5_1KKFWLGE" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=v5_1KKFWLGE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Services &amp;amp; Networking walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;Here is the setup. A Deployment named web sits behind a Service, and an Ingress named web terminates TLS for the host gateway.web.k8s.local and routes to it. A GatewayClass is already installed. Your task is to migrate this to the Gateway API, keep the same HTTPS host, and once it works, delete the old Ingress.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ingress named 'web' serves HTTPS for gateway.web.k8s.local&lt;/li&gt;
&lt;li&gt;A GatewayClass is already installed in the cluster&lt;/li&gt;
&lt;li&gt;Recreate the routing with a Gateway + HTTPRoute&lt;/li&gt;
&lt;li&gt;Keep HTTPS, then delete the old Ingress&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How an Ingress maps onto the Gateway API
&lt;/h2&gt;

&lt;p&gt;The Gateway API splits what one Ingress did into three objects. The GatewayClass is the controller, like the ingress class. The Gateway is the listener: the port and the TLS the Ingress used to own. The HTTPRoute holds the routing rules: the hostname, the paths, and the backend Service. Map the Ingress onto those three and the migration is mechanical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the YAML comes from (you author it, not &lt;code&gt;apply -f&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;There is no imperative &lt;code&gt;kubectl create&lt;/code&gt; for a Gateway or HTTPRoute, so in the exam you&lt;br&gt;
write the YAML yourself, copying a starting template from the Gateway API docs (linked from&lt;br&gt;
the Kubernetes documentation, which is allowed during the exam):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gateway + TLS:&lt;/strong&gt; &lt;a href="https://gateway-api.sigs.k8s.io/guides/" rel="noopener noreferrer"&gt;Simple Gateway&lt;/a&gt; and
&lt;a href="https://gateway-api.sigs.k8s.io/guides/tls/" rel="noopener noreferrer"&gt;TLS termination&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPRoute:&lt;/strong&gt; &lt;a href="https://gateway-api.sigs.k8s.io/guides/http-routing/" rel="noopener noreferrer"&gt;HTTP routing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field reference:&lt;/strong&gt; &lt;a href="https://gateway-api.sigs.k8s.io/reference/spec/" rel="noopener noreferrer"&gt;API spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From kubernetes.io:&lt;/strong&gt; &lt;a href="https://kubernetes.io/docs/concepts/services-networking/gateway/" rel="noopener noreferrer"&gt;Gateway API&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every value is something you already have. &lt;code&gt;gatewayClassName&lt;/code&gt; is the class from&lt;br&gt;
&lt;code&gt;kubectl get gatewayclass&lt;/code&gt;. The HTTPS listener port and the &lt;code&gt;certificateRefs&lt;/code&gt; Secret are&lt;br&gt;
carried over from the existing Ingress's &lt;code&gt;tls&lt;/code&gt; block. The &lt;code&gt;hostname&lt;/code&gt;, the path, and the&lt;br&gt;
backend Service (&lt;code&gt;web:80&lt;/code&gt;) are the Ingress's own routing rule, re-expressed as an HTTPRoute.&lt;br&gt;
You can see both files (&lt;code&gt;cat&lt;/code&gt;) right before each &lt;code&gt;kubectl apply&lt;/code&gt; below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Inspect what's running
&lt;/h2&gt;

&lt;p&gt;Start by reading the current state. The web Deployment and Service are running, and the Ingress named web is serving the host gateway.web.k8s.local on ports 80 and 443. The GatewayClass is present and Accepted, so the controller is ready for a Gateway to bind to it.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; web get deploy,svc,ingress
&lt;span class="go"&gt;NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tester   1/1     1            1           117m
deployment.apps/web      1/1     1            1           117m

NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
&lt;/span&gt;&lt;span class="gp"&gt;service/web   ClusterIP   10.96.31.154   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;80/TCP    117m
&lt;span class="go"&gt;
NAME                            CLASS   HOSTS                   ADDRESS     PORTS     AGE
ingress.networking.k8s.io/web   nginx   gateway.web.k8s.local   localhost   80, 443   74m

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get gatewayclass
&lt;span class="go"&gt;NAME   CONTROLLER                                      ACCEPTED   AGE
eg     gateway.envoyproxy.io/gatewayclass-controller   True       117m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Ingress serves HTTPS today
&lt;/h2&gt;

&lt;p&gt;Prove the starting point. A client that resolves gateway.web.k8s.local to the Ingress controller gets the page back over HTTPS, returning WEB APP OK. This is the behavior we must preserve through the migration.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://gateway.web.k8s.local/
&lt;span class="go"&gt;WEB APP OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Gateway
&lt;/h2&gt;

&lt;p&gt;There is no kubectl create for these, so you author the YAML by hand from the Gateway API docs, the Simple Gateway and TLS termination examples, which are linked from the Kubernetes docs. Look at the file. apiVersion and kind declare a Gateway. The gatewayClassName is eg, the class you just saw was Accepted. Then one listener: HTTPS on port 443 for the hostname gateway.web.k8s.local. Under tls, mode Terminate with a certificateRef to web-tls, the very same Secret the Ingress used, so HTTPS is preserved. Every value came from the GatewayClass or the old Ingress. Apply it and wait until it reports Programmed, which means the controller has provisioned the data plane and assigned an address.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;gateway.yaml
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Gateway: an HTTPS listener on 443 that terminates TLS with the web-tls Secret.
&lt;span class="go"&gt;apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: web
spec:
  gatewayClassName: eg
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: gateway.web.k8s.local
      tls:
        mode: Terminate
        certificateRefs:
          - name: web-tls

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; gateway.yaml
&lt;span class="go"&gt;gateway.gateway.networking.k8s.io/web-gateway created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; web get gateway web-gateway
&lt;span class="go"&gt;NAME          CLASS   ADDRESS         PROGRAMMED   AGE
web-gateway   eg      10.96.138.181   True         9s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the HTTPRoute
&lt;/h2&gt;

&lt;p&gt;Now the routing, from the docs HTTP routing example. Read the file. It is an HTTPRoute. parentRefs attaches it to the web-gateway you just made, so this route is served by that Gateway. hostnames matches gateway.web.k8s.local, the same host as before. And the one rule matches the path prefix slash and sends it to backendRefs, the web Service on port 80. The host, the path, and the backend are lifted straight from the old Ingress rule. Apply it and confirm the hostname is bound.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;httproute.yaml
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;HTTPRoute: same host as the Ingress, path / -&amp;gt; the web Service on port 80.
&lt;span class="go"&gt;apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-route
  namespace: web
spec:
  parentRefs:
    - name: web-gateway
  hostnames:
    - gateway.web.k8s.local
  rules:
    - matches:
        - path:
            value: /
      backendRefs:
        - name: web
          port: 80

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; httproute.yaml
&lt;span class="go"&gt;httproute.gateway.networking.k8s.io/web-route created

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; web get httproute web-route
&lt;span class="go"&gt;NAME        HOSTNAMES                   AGE
web-route   ["gateway.web.k8s.local"]   0s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify, then delete the Ingress
&lt;/h2&gt;

&lt;p&gt;Verify before you remove anything. The same request, now routed through the Gateway, returns WEB APP OK over HTTPS. Only once that works do you delete the old Ingress. Test one more time: traffic still flows through the Gateway, with the Ingress gone. The migration is complete with no outage.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://gateway.web.k8s.local/
&lt;span class="go"&gt;WEB APP OK

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; web delete ingress web
&lt;span class="go"&gt;ingress.networking.k8s.io "web" deleted from web namespace

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://gateway.web.k8s.local/
&lt;span class="go"&gt;WEB APP OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps. There is no kubectl create for Gateways or HTTPRoutes, so keep the Gateway API docs open and copy the manifests. Reuse the same TLS Secret on the Gateway listener so HTTPS keeps working. Wait for Programmed before testing, or you will curl a listener that is not up yet. And delete the Ingress last, only after the Gateway is verified, so you never drop traffic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No imperative command: copy Gateway + HTTPRoute YAML from gateway-api.sigs.k8s.io&lt;/li&gt;
&lt;li&gt;Reuse the same TLS Secret on the Gateway listener to keep HTTPS&lt;/li&gt;
&lt;li&gt;Wait for the Gateway to be Programmed before you test&lt;/li&gt;
&lt;li&gt;Delete the Ingress LAST, only after the Gateway is verified&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gateway = listener + TLS; HTTPRoute = host + path -&amp;gt; Service&lt;/li&gt;
&lt;li&gt;Reuse the TLS Secret; wait for Programmed&lt;/li&gt;
&lt;li&gt;Verify HTTPS, then delete the Ingress last&lt;/li&gt;
&lt;li&gt;Subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario6-ingress-to-gateway
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>gatewayapi</category>
      <category>networking</category>
    </item>
    <item>
      <title>CKA Scenario 5 - Force nginx to TLS 1.3 with a ConfigMap edit + rolling restart (CKA Workloads)</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Mon, 29 Jun 2026 12:53:22 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/force-nginx-to-tls-13-with-a-configmap-edit-rolling-restart-cka-workloads-4cmb</link>
      <guid>https://dev.to/thecybersidekick/force-nginx-to-tls-13-with-a-configmap-edit-rolling-restart-cka-workloads-4cmb</guid>
      <description>&lt;h2&gt;
  
  
  Force nginx to TLS 1.3
&lt;/h2&gt;

&lt;p&gt;An nginx server is accepting an old TLS version, and the exam wants it locked to TLS one point three. The config lives in a ConfigMap. The catch is that editing the ConfigMap alone changes nothing. Let's do it the way the CKA expects.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch the video:&lt;/strong&gt; &lt;a href="https://www.youtube.com/watch?v=rx-77YBw99w" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=rx-77YBw99w&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a CKA Workloads &amp;amp; Scheduling walkthrough. Every command below is real output from a live cluster, and you can reproduce the whole thing yourself (scripts at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;An nginx-static Deployment serves HTTPS, and its server config comes from a ConfigMap named nginx-config. Right now it allows both TLS one point two and one point three. Your task is to allow only TLS one point three, then make nginx actually use the change, so that a TLS one point two request fails.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nginx-static serves HTTPS from the nginx-config ConfigMap&lt;/li&gt;
&lt;li&gt;It currently allows TLS 1.2 AND 1.3&lt;/li&gt;
&lt;li&gt;Restrict ssl_protocols to TLS 1.3 only&lt;/li&gt;
&lt;li&gt;A TLS 1.2 request to the Service must then fail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How nginx, ConfigMaps, and rolling restarts fit together
&lt;/h2&gt;

&lt;p&gt;Two ideas drive this. First, ssl_protocols is an allow list; leave only TLSv1.3 and nginx rejects any older handshake. Second, a ConfigMap mounted into a pod updates the file on disk, but nginx only reads ssl_protocols when it starts. So you must roll the Deployment, with kubectl rollout restart, for the new value to take effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect the current state
&lt;/h2&gt;

&lt;p&gt;Start by seeing what is running and what the config says. The nginx-static Deployment, its Service on port four forty three, and the nginx-config ConfigMap are all here. Grep the rendered ConfigMap for the ssl_protocols line: it lists TLSv1.2 and TLSv1.3, so old clients still get in.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static get deploy,svc,configmap
&lt;span class="go"&gt;NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-static   1/1     1            1           17h
deployment.apps/tester         1/1     1            1           17h

NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
&lt;/span&gt;&lt;span class="gp"&gt;service/nginx-static   ClusterIP   10.96.13.162   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;443/TCP   17h
&lt;span class="go"&gt;
NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      17h
configmap/nginx-config       1      2s

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static get configmap nginx-config &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | &lt;span class="nb"&gt;grep &lt;/span&gt;ssl_protocols
&lt;span class="gp"&gt;        ssl_protocols TLSv1.2 TLSv1.3;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Confirm TLS 1.2 works today
&lt;/h2&gt;

&lt;p&gt;Prove the starting point from a real client. The tester pod curls the Service, pinned to a maximum of TLS one point two. It returns the page, which confirms the server accepts TLS one point two right now. That is exactly what we are about to stop.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static &lt;span class="nb"&gt;exec &lt;/span&gt;deploy/tester &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;-sk&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;--tls-max&lt;/span&gt; 1.2 https://nginx-static.nginx-static.svc.cluster.local
&lt;span class="go"&gt;TLS OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Make nginx use it. A rolling restart replaces the pod, and the new pod reads the updated ssl_protocols on startup. Wait for the rollout to finish so you are testing the new pod, not the old one. This is the step people skip, and it is where the marks are.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static rollout restart deploy/nginx-static
&lt;span class="go"&gt;deployment.apps/nginx-static restarted

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static rollout status deploy/nginx-static
&lt;span class="go"&gt;Waiting for deployment "nginx-static" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-static" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-static" successfully rolled out
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify
&lt;/h2&gt;

&lt;p&gt;Prove it both ways. The same TLS one point two request now fails the handshake; curl reports an alert and exits non-zero, which is what we want. A normal request, letting curl negotiate, connects over TLS one point three and still returns the page. Old TLS is gone, the service still works.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static &lt;span class="nb"&gt;exec &lt;/span&gt;deploy/tester &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;-sSk&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;--tls-max&lt;/span&gt; 1.2 https://nginx-static.nginx-static.svc.cluster.local
&lt;span class="go"&gt;curl: (35) OpenSSL/3.3.2: error:0A00042E:SSL routines::tlsv1 alert protocol version
command terminated with exit code 35

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; nginx-static &lt;span class="nb"&gt;exec &lt;/span&gt;deploy/tester &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;-skv&lt;/span&gt; https://nginx-static.nginx-static.svc.cluster.local | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'SSL connection|TLS OK'&lt;/span&gt;
&lt;span class="go"&gt;* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS
TLS OK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exam tips
&lt;/h2&gt;

&lt;p&gt;A few traps to remember. Editing a ConfigMap does not restart anything; without a rollout restart your change is invisible. ssl_protocols is an allow list, so list only the versions you want. Test with the client version pinned, because curl will quietly use TLS one point three and hide the problem otherwise. And always verify against the Service name, not just the pod.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editing a ConfigMap does nothing until you roll the Deployment&lt;/li&gt;
&lt;li&gt;ssl_protocols is an allow-list: leave only TLSv1.3&lt;/li&gt;
&lt;li&gt;Pin the client (curl --tls-max 1.2) or you won't see the failure&lt;/li&gt;
&lt;li&gt;Verify against the Service, not just the pod&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set ssl_protocols to TLSv1.3 in the ConfigMap&lt;/li&gt;
&lt;li&gt;kubectl rollout restart so nginx re-reads it&lt;/li&gt;
&lt;li&gt;Verify TLS 1.2 fails, TLS 1.3 still serves&lt;/li&gt;
&lt;li&gt;Subscribe + dev.to writeup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce this yourself
&lt;/h2&gt;

&lt;p&gt;The entire scenario is scripted on a throwaway &lt;code&gt;kind&lt;/code&gt; cluster: &lt;a href="https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios" rel="noopener noreferrer"&gt;https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/The-Cyber-Sidekick/TCS_CKA_2026_Exam_Scenarios.git
&lt;span class="nb"&gt;cd &lt;/span&gt;TCS_CKA_2026_Exam_Scenarios/learning/scenarios/scenario5-nginx-tls-configmap
./setup.sh        &lt;span class="c"&gt;# creates the cluster AND arms the scenario&lt;/span&gt;
&lt;span class="c"&gt;# solve it by hand, or:&lt;/span&gt;
./solution.sh     &lt;span class="c"&gt;# apply the answer key and verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If this helped, &lt;strong&gt;subscribe to The Cyber SideKick&lt;/strong&gt; on YouTube for more CKA drills, and grab the newsletter at &lt;a href="https://thecybersidekick.beehiiv.com" rel="noopener noreferrer"&gt;https://thecybersidekick.beehiiv.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>devops</category>
      <category>tls</category>
    </item>
    <item>
      <title>AI-Driven DevOps Is Reshaping CI/CD: From Pipeline Mechanics to Autonomous Orchestration</title>
      <dc:creator>The Cyber Sidekick</dc:creator>
      <pubDate>Mon, 29 Jun 2026 11:31:15 +0000</pubDate>
      <link>https://dev.to/thecybersidekick/ai-driven-devops-is-reshaping-cicd-from-pipeline-mechanics-to-autonomous-orchestration-3m3d</link>
      <guid>https://dev.to/thecybersidekick/ai-driven-devops-is-reshaping-cicd-from-pipeline-mechanics-to-autonomous-orchestration-3m3d</guid>
      <description>&lt;p&gt;&lt;em&gt;How ML agents and LLM-powered observability are moving DevOps teams from reactive pipeline management to predictive, self-healing infrastructure automation.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI-driven DevOps is eliminating manual CI/CD bottlenecks by turning pipelines into autonomous systems that detect, diagnose, and fix deployment issues before they reach production. The convergence of large language models, ML-based anomaly detection, and durable workflow orchestration is compressing mean-time-to-recovery from hours to minutes, with Gartner projecting that 40% of large enterprises will autonomously resolve infrastructure incidents without human intervention by 2027.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Reactive Pipeline Problem and Why It Is Breaking Under Modern Scale
&lt;/h2&gt;

&lt;p&gt;Traditional DevOps pipelines are fundamentally reactive: alerts fire after production metrics degrade, rollbacks trigger after error budgets are burned, and on-call engineers diagnose failures that users have already encountered. This approach creates mean-time-to-recovery gaps measured in minutes to hours, with threshold-based alerting generating noise that masks real signals until damage is done. The structural problem is that pipelines were designed as linear executors, not intelligent decision-makers, so every anomaly outside a predefined threshold requires human judgment to classify, prioritize, and remediate. Organizations using ML-based anomaly detection on deployment pipelines are reporting mean-time-to-detect reductions of 60 to 70 percent compared to threshold-based alerting, according to Dynatrace's 2024 State of Observability report, which illustrates the scale of the opportunity left untapped by conventional tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Emerging AI-Agentic Infrastructure Stack
&lt;/h2&gt;

&lt;p&gt;The ecosystem is moving rapidly from AI-assisted tooling toward AI-agentic infrastructure, where platforms make autonomous decisions within policy-encoded boundaries rather than merely surfacing recommendations to humans. Dynatrace Davis combines causal AI topology mapping with LLM-generated root cause explanations, correlating logs, traces, and metrics simultaneously rather than in isolation. GitOps controllers like Argo CD are being extended with Keptn integrations that evaluate deployment risk scores derived from historical telemetry and automatically pause or roll back Helm releases based on SLO breach signals, effectively encoding SRE judgment as executable policy. Temporal.io has emerged as a critical durable execution backbone for these autonomous remediation agents, providing retry semantics, state persistence, and full workflow auditability across multi-step sequences; the platform reported over 500 billion workflow actions executed in 2024, reflecting how quickly durable orchestration is becoming the control plane for complex automated remediation. Startups including Cortex, Harness, and Port are layering ML models trained on deployment patterns directly into internal developer portals, surfacing reliability recommendations before code reaches merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Trends Defining the Next Generation of Intelligent Pipelines
&lt;/h2&gt;

&lt;p&gt;Four converging trends are shaping how AI integrates into DevOps workflows at scale. First, the standardization of OpenTelemetry as a unified telemetry substrate is giving AI models consistent, vendor-agnostic data to reason over, removing the fragmentation that previously made cross-stack correlation impractical. Second, GitOps-native AI policy engines are encoding remediation runbooks as version-controlled code reviewed alongside application manifests, making autonomous decisions auditable and reversible through standard pull request workflows. Third, SRE copilots powered by LLMs fine-tuned on incident postmortems, Kubernetes event streams, and infrastructure runbooks are generating contextual remediation playbooks in real time, reducing the cognitive load on engineers during active incidents. Fourth, the combination of these signals into unified AI observability agents is enabling platforms to move from detecting that something is wrong to explaining why it is wrong and executing a fix, all within a single automated feedback loop.&lt;/p&gt;

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

&lt;p&gt;The trajectory of AI-driven DevOps points toward infrastructure that is less a pipeline to be managed and more an autonomous system to be governed. The foundational pieces are already in production: OpenTelemetry provides the data substrate, Temporal provides the execution durability, Argo CD and Keptn provide the GitOps enforcement layer, and LLMs provide the contextual reasoning that previously required senior engineers. The near-term challenge for platform teams is not adoption but governance: defining the policy boundaries within which AI agents are permitted to act autonomously, ensuring auditability trails satisfy compliance requirements, and building the human-in-the-loop escalation paths that preserve trust when autonomous decisions fail. With Gartner projecting that fewer than 5% of enterprises autonomously resolve infrastructure incidents today versus 40% by 2027, the organizations that invest now in durable orchestration, telemetry standardization, and AI policy frameworks will hold a compounding reliability and velocity advantage over those still waiting for the tooling to mature.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies covered:&lt;/strong&gt; LLMs for log analysis and root cause detection, ML-based anomaly detection in deployment patterns, Autonomous workflow orchestration (Temporal, Dagster), GitOps + AI decision engines, Observability platforms with AI correlation&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sources aggregated from: DevOps Weekly, GitHub Trending, Hacker News, The New Stack&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  📬 Stay current with cloud-native
&lt;/h3&gt;

&lt;p&gt;Get the latest Kubernetes, DevOps, and platform engineering insights delivered to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://thecybersidekick.beehiiv.com/subscribe" rel="noopener noreferrer"&gt;Subscribe to The Cyber SideKick Newsletter&lt;/a&gt;&lt;/strong&gt; — free, no spam, unsubscribe anytime.&lt;/p&gt;

</description>
      <category>aidrivendevops</category>
      <category>cicdautomation</category>
      <category>llmloganalysis</category>
      <category>mlanomalydetection</category>
    </item>
  </channel>
</rss>
