<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Selma Guedidi</title>
    <description>The latest articles on Forem by Selma Guedidi (@selmaguedidi).</description>
    <link>https://forem.com/selmaguedidi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1170049%2F558ceeb3-6337-4248-85c3-b29f68a3bb44.png</url>
      <title>Forem: Selma Guedidi</title>
      <link>https://forem.com/selmaguedidi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/selmaguedidi"/>
    <language>en</language>
    <item>
      <title>How to use Kubectl more efficiently</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Mon, 04 May 2026 11:26:53 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/-1njm</link>
      <guid>https://forem.com/selmaguedidi/-1njm</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d" class="crayons-story__hidden-navigation-link"&gt;kubectl Hacks That Changed How I Work With Kubernetes&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/selmaguedidi" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1170049%2F558ceeb3-6337-4248-85c3-b29f68a3bb44.png" alt="selmaguedidi profile" class="crayons-avatar__image" width="420" height="420"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/selmaguedidi" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Selma Guedidi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Selma Guedidi
                
              
              &lt;div id="story-author-preview-content-3558101" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/selmaguedidi" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1170049%2F558ceeb3-6337-4248-85c3-b29f68a3bb44.png" class="crayons-avatar__image" alt="" width="420" height="420"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Selma Guedidi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d" id="article-link-3558101"&gt;
          kubectl Hacks That Changed How I Work With Kubernetes
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/kubernetes"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;kubernetes&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/containers"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;containers&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/automation"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;automation&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            12 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>kubernetes</category>
      <category>shell</category>
      <category>devops</category>
    </item>
    <item>
      <title>kubectl Hacks That Changed How I Work With Kubernetes</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Wed, 29 Apr 2026 19:27:03 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d</link>
      <guid>https://forem.com/selmaguedidi/kubectl-hacks-that-changed-how-i-work-with-kubernetes-4p8d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Beyond the basics: patching, waiting, explaining, and squeezing the most out of the one tool that never leaves your terminal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most kubectl guides stop at &lt;code&gt;get pods&lt;/code&gt; and &lt;code&gt;apply -f&lt;/code&gt;. That's fine for getting started, but after years of running Kubernetes in production, the commands that make the &lt;em&gt;real&lt;/em&gt; difference are the ones nobody writes about. The obscure flags. The output tricks. The commands that turn a 20-minute debugging slog into a 90-second fix.&lt;/p&gt;

&lt;p&gt;This article is about the depth of kubectl: commands I reach for when things get weird, scripting patterns that made our CI pipelines solid, and the shorthand vocabulary that turns the tool from a keyboard workout into something that actually feels fast. No fluff, no padding. Just the stuff worth knowing.&lt;/p&gt;




&lt;h2&gt;
  
  
  01 — The Complete Shorthand Vocabulary
&lt;/h2&gt;

&lt;p&gt;Before anything else: the complete list of resource type abbreviations. These are baked into the Kubernetes API itself, so they work universally across &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;edit&lt;/code&gt;, and every other verb. Memorise the ones you use daily and your hands will thank you.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Short&lt;/th&gt;
&lt;th&gt;Full Resource Name&lt;/th&gt;
&lt;th&gt;API Group&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;po&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;pods&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;svc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;services&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;deployments&lt;/td&gt;
&lt;td&gt;apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;daemonsets&lt;/td&gt;
&lt;td&gt;apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;replicasets&lt;/td&gt;
&lt;td&gt;apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;statefulsets&lt;/td&gt;
&lt;td&gt;apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;namespaces&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;configmaps&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ingresses&lt;/td&gt;
&lt;td&gt;networking.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;persistentvolumes&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pvc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;persistentvolumeclaims&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;serviceaccounts&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;endpoints&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;nodes&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;events&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hpa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;horizontalpodautoscalers&lt;/td&gt;
&lt;td&gt;autoscaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pdb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;poddisruptionbudgets&lt;/td&gt;
&lt;td&gt;policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;crd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;customresourcedefinitions&lt;/td&gt;
&lt;td&gt;apiextensions.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;crb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;clusterrolebindings&lt;/td&gt;
&lt;td&gt;rbac.authorization.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rolebindings&lt;/td&gt;
&lt;td&gt;rbac.authorization.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netpol&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;networkpolicies&lt;/td&gt;
&lt;td&gt;networking.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;storageclasses&lt;/td&gt;
&lt;td&gt;storage.k8s.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;job&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;jobs&lt;/td&gt;
&lt;td&gt;batch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cj&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;cronjobs&lt;/td&gt;
&lt;td&gt;batch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quota&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;resourcequotas&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;secrets&lt;/td&gt;
&lt;td&gt;core&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pro tip:&lt;/strong&gt; Run &lt;code&gt;kubectl api-resources&lt;/code&gt; in your cluster to see every available shorthand, including those from installed CRDs. It shows the short name, API group, whether the resource is namespaced, and the kind.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  02 — kubectl patch: The Surgeon's Tool
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;kubectl apply&lt;/code&gt; is great when you own the full manifest. But what about changing a single field on a resource managed by a Helm chart, an operator, or another team? You don't want to touch the whole file. You want a scalpel. That's &lt;code&gt;kubectl patch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are three patch types, and picking the wrong one will lose you data or produce confusing merges.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strategic-merge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes-aware merge. Containers are merged by name, not replaced.&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Default. Use most of the time.&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;merge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RFC 7396 JSON Merge. Arrays are replaced entirely, not merged.&lt;/td&gt;
&lt;td&gt;CRDs and non-core resources.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RFC 6902. Precise add / remove / replace / move operations.&lt;/td&gt;
&lt;td&gt;When you need surgical precision.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add an annotation without touching the rest of the manifest&lt;/span&gt;
kubectl patch deploy api-gateway &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"metadata":{"annotations":{"deploy-time":"2026-04-27"}}}'&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Change replica count on the fly (useful in incidents)&lt;/span&gt;
kubectl patch deploy api-gateway &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"replicas":6}}'&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Remove a field entirely with JSON patch&lt;/span&gt;
kubectl patch deploy api-gateway &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'[{"op":"remove","path":"/spec/template/spec/containers/0/resources/limits/cpu"}]'&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Replace a specific env var value by index&lt;/span&gt;
kubectl patch deploy api-gateway &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'[{"op":"replace","path":"/spec/template/spec/containers/0/env/0/value","value":"https://new-db-host"}]'&lt;/span&gt;

&lt;span class="c"&gt;# Cordon a node : what kubectl cordon does under the hood&lt;/span&gt;
kubectl patch node ip-10-0-1-47 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"unschedulable":true}}'&lt;/span&gt;

&lt;span class="c"&gt;# Add a label to a node using json patch&lt;/span&gt;
kubectl patch node ip-10-0-1-47 &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'[{"op":"add","path":"/metadata/labels/topology.kubernetes.io~1zone","value":"us-east-1a"}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Watch out:&lt;/strong&gt; The &lt;code&gt;/&lt;/code&gt; character in JSON Patch paths refers to object keys. If your key contains a forward slash (like &lt;code&gt;kubernetes.io/role&lt;/code&gt;), escape it as &lt;code&gt;~1&lt;/code&gt;. The tilde &lt;code&gt;~&lt;/code&gt; itself escapes as &lt;code&gt;~0&lt;/code&gt;. Easy to forget, expensive when wrong.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  03 — kubectl wait: The CI/CD Game Changer
&lt;/h2&gt;

&lt;p&gt;If you write shell scripts or CI pipelines that deploy to Kubernetes, you've probably written something like: deploy → sleep 30 → check status → sleep more → give up. It's flaky and slow. &lt;code&gt;kubectl wait&lt;/code&gt; replaces all of that with a proper event-driven block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Block until a deployment finishes rolling out (timeout after 3 minutes)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;deploy/api-gateway &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Available &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3m &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Wait for all pods with a label to be Ready&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;pods &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;api-gateway &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Ready &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2m &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Wait for a Job to complete&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;job/db-migration &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Complete &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10m &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Wait for a Job to fail (useful for catching broken migrations early)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;job/db-migration &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Failed &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10m &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Wait for a node to be Ready after a restart&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;node/ip-10-0-1-47 &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Ready &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5m

&lt;span class="c"&gt;# Wait for a CRD to be established (important in Helm chart init containers)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;crd/certificates.cert-manager.io &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Established &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s

&lt;span class="c"&gt;# Wait for a pod to be deleted&lt;/span&gt;
kubectl &lt;span class="nb"&gt;wait &lt;/span&gt;pod/old-pod-abc123 &lt;span class="nt"&gt;--for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;delete &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--for=delete&lt;/code&gt; form is especially valuable. Whenever you need to drain a node or delete a pod and then take some action only &lt;em&gt;after&lt;/em&gt; it's truly gone, this is your signal. No polling loops, no racy sleep calls.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CI pattern:&lt;/strong&gt; Chain &lt;code&gt;kubectl apply&lt;/code&gt; and &lt;code&gt;kubectl wait&lt;/code&gt; in your pipeline. If the wait times out, the pipeline fails with a non-zero exit code, no extra health-check scripts needed. You get free deploy verification.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  04 — kubectl explain: The Docs Are Already in Your Terminal
&lt;/h2&gt;

&lt;p&gt;Every time I see someone switching to a browser to look up what a field does in a Kubernetes spec, I cringe. The full API reference is built right into &lt;code&gt;kubectl explain&lt;/code&gt;. It supports dot-notation for nested fields and &lt;code&gt;--recursive&lt;/code&gt; to print the entire schema tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What is a PodSpec?&lt;/span&gt;
kubectl explain pod.spec

&lt;span class="c"&gt;# What fields does a container accept?&lt;/span&gt;
kubectl explain pod.spec.containers

&lt;span class="c"&gt;# Go deep on a specific field&lt;/span&gt;
kubectl explain pod.spec.containers.resources.requests

&lt;span class="c"&gt;# Print the entire schema tree for a resource&lt;/span&gt;
kubectl explain deployment &lt;span class="nt"&gt;--recursive&lt;/span&gt;

&lt;span class="c"&gt;# Works on CRDs too&lt;/span&gt;
kubectl explain certificate.spec.renewBefore

&lt;span class="c"&gt;# Check what API version a resource uses&lt;/span&gt;
kubectl explain horizontalpodautoscaler &lt;span class="nt"&gt;--api-version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;autoscaling/v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--recursive&lt;/code&gt; output looks dense at first but becomes invaluable when you're unfamiliar with a CRD's schema. Pipe it through &lt;code&gt;grep&lt;/code&gt; to find the field you care about rather than reading a thousand-line GitHub README.&lt;/p&gt;




&lt;h2&gt;
  
  
  05 — kubectl cp: Moving Files In and Out of Containers
&lt;/h2&gt;

&lt;p&gt;This one saves you in two specific scenarios: extracting a generated file from a container (a rendered config, a certificate, a data dump), and pushing a temporary script into a running pod when you can't rebuild the image. It behaves like &lt;code&gt;scp&lt;/code&gt; with a slightly different path syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy a file OUT of a container&lt;/span&gt;
kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;production/api-pod-7d4f8b:/app/logs/error.log ./error.log

&lt;span class="c"&gt;# Copy a file INTO a container&lt;/span&gt;
kubectl &lt;span class="nb"&gt;cp&lt;/span&gt; ./debug-script.sh production/api-pod-7d4f8b:/tmp/debug-script.sh

&lt;span class="c"&gt;# Copy a whole directory out&lt;/span&gt;
kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;production/api-pod-7d4f8b:/app/data ./data-backup

&lt;span class="c"&gt;# Specify a container in a multi-container pod&lt;/span&gt;
kubectl &lt;span class="nb"&gt;cp &lt;/span&gt;production/api-pod-7d4f8b:/var/log/app.log ./app.log &lt;span class="nt"&gt;-c&lt;/span&gt; sidecar-logger

&lt;span class="c"&gt;# Format: [namespace/]pod-name:path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Heads up:&lt;/strong&gt; &lt;code&gt;kubectl cp&lt;/code&gt; requires &lt;code&gt;tar&lt;/code&gt; to be present inside the container. Distroless images don't have it. If you hit that wall, use &lt;code&gt;kubectl debug&lt;/code&gt; to attach a debug sidecar first, then copy from there.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  06 — Generating YAML Without Writing YAML
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;--dry-run=client -o yaml&lt;/code&gt; pattern lets kubectl write your manifests for you. Here's a full set of imperative-to-declarative shortcuts that produce valid, committable YAML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deployment manifest&lt;/span&gt;
kubectl create deploy api-gateway &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/api:v2.3 &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deployment.yaml

&lt;span class="c"&gt;# Service (ClusterIP)&lt;/span&gt;
kubectl create svc clusterip api-gateway &lt;span class="nt"&gt;--tcp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80:8080 &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;# Service (LoadBalancer)&lt;/span&gt;
kubectl create svc loadbalancer api-lb &lt;span class="nt"&gt;--tcp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;443:8443 &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;# ConfigMap from file and literals&lt;/span&gt;
kubectl create cm app-config &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eu-west-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml

&lt;span class="c"&gt;# Secret (generic)&lt;/span&gt;
kubectl create secret generic db-creds &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hunter2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml

&lt;span class="c"&gt;# Secret (TLS)&lt;/span&gt;
kubectl create secret tls api-tls &lt;span class="nt"&gt;--cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.crt &lt;span class="nt"&gt;--key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.key &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;# Job&lt;/span&gt;
kubectl create job db-migration &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/migrator:v1 &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;# CronJob&lt;/span&gt;
kubectl create cronjob nightly-report &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/reporter:v1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--schedule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0 2 * * *"&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml

&lt;span class="c"&gt;# ServiceAccount&lt;/span&gt;
kubectl create sa api-worker &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;# Role&lt;/span&gt;
kubectl create role pod-reader &lt;span class="nt"&gt;--verb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;get,list,watch &lt;span class="nt"&gt;--resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pods &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;# RoleBinding&lt;/span&gt;
kubectl create rolebinding api-pod-reader &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pod-reader &lt;span class="nt"&gt;--serviceaccount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production:api-worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  07 — kubectl set: In-Place Updates Without Editing YAML
&lt;/h2&gt;

&lt;p&gt;When you need to change a single well-defined property on a running resource, &lt;code&gt;kubectl set&lt;/code&gt; is cleaner than patching raw JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update the image of a single container&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deploy/api-gateway &lt;span class="nv"&gt;api&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/api:v2.4 &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Update multiple containers at once&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deploy/api-gateway &lt;span class="nv"&gt;api&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/api:v2.4 &lt;span class="nv"&gt;sidecar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ghcr.io/myorg/proxy:v1.1

&lt;span class="c"&gt;# Set resource requests and limits&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;resources deploy/api-gateway &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--requests&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;250m,memory&lt;span class="o"&gt;=&lt;/span&gt;512Mi &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--limits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000m,memory&lt;span class="o"&gt;=&lt;/span&gt;1Gi &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Assign a service account&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;serviceaccount deploy/api-gateway api-worker &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Add or update an environment variable&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set env &lt;/span&gt;deploy/api-gateway &lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Set env from a ConfigMap&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set env &lt;/span&gt;deploy/api-gateway &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;configmap/app-config &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Set env from a Secret&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set env &lt;/span&gt;deploy/api-gateway &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret/db-creds &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Remove an env var (suffix with a minus)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set env &lt;/span&gt;deploy/api-gateway LOG_LEVEL- &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;kubectl set image&lt;/code&gt; records the change in the deployment's rollout history, which means you can &lt;code&gt;kubectl rollout undo&lt;/code&gt; it just like a &lt;code&gt;kubectl apply&lt;/code&gt;. It's not a dirty in-place hack, it's a first-class operation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  08 — Labels, Annotations, and Taints: The Metadata Commands
&lt;/h2&gt;

&lt;p&gt;Labels and annotations are the connective tissue of Kubernetes. Services find their pods through labels. Operators read annotations for config. Knowing how to manipulate this metadata live, without opening a YAML file, is surprisingly powerful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add a label to a pod&lt;/span&gt;
kubectl label pod api-pod-7d4f8b &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v2.4 &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Overwrite an existing label&lt;/span&gt;
kubectl label pod api-pod-7d4f8b &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v2.5 &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;--overwrite&lt;/span&gt;

&lt;span class="c"&gt;# Remove a label (suffix with a minus)&lt;/span&gt;
kubectl label pod api-pod-7d4f8b version- &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Label all pods matching a selector&lt;/span&gt;
kubectl label pods &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;api-gateway &lt;span class="nv"&gt;canary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Label a node for topology-aware scheduling&lt;/span&gt;
kubectl label node ip-10-0-1-47 &lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssd &lt;span class="nv"&gt;zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1a

&lt;span class="c"&gt;# Add an annotation&lt;/span&gt;
kubectl annotate deploy/api-gateway &lt;span class="se"&gt;\&lt;/span&gt;
  deployment.kubernetes.io/revision-message&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"hotfix: null pointer on /checkout"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Taint a node so only tolerating pods schedule on it&lt;/span&gt;
kubectl taint node ip-10-0-1-47 &lt;span class="nv"&gt;dedicated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gpu-workloads:NoSchedule

&lt;span class="c"&gt;# Remove a taint (suffix the effect with a minus)&lt;/span&gt;
kubectl taint node ip-10-0-1-47 dedicated:NoSchedule-

&lt;span class="c"&gt;# Cordon and drain before maintenance&lt;/span&gt;
kubectl cordon ip-10-0-1-47
kubectl drain ip-10-0-1-47 &lt;span class="nt"&gt;--ignore-daemonsets&lt;/span&gt; &lt;span class="nt"&gt;--delete-emptydir-data&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
kubectl uncordon ip-10-0-1-47  &lt;span class="c"&gt;# after maintenance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  09 — Output Tricks: Beyond the Default Table
&lt;/h2&gt;

&lt;p&gt;The default tabular output is readable, but when you're scripting or building dashboards, you need exact fields. Here are the output modes worth knowing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full resource as YAML. Great for inspection and editing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full resource as JSON. Pipe to &lt;code&gt;jq&lt;/code&gt; for advanced queries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;resource/name&lt;/code&gt; format. Ideal for piping into delete or apply.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o jsonpath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract specific fields. Best for scripts and alerts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o wide&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extra columns: node, IP, nominated node, readiness gates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o custom-columns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define your own table headers and field paths.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull a single field cleanly for scripting&lt;/span&gt;
kubectl get deploy api-gateway &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.replicas}'&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Get all image names running across a namespace&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{range .items[*]}{.metadata.name}{"  "}{range .spec.containers[*]}{.image}{"\n"}{end}{end}'&lt;/span&gt;

&lt;span class="c"&gt;# Get the cluster API server URL&lt;/span&gt;
kubectl config view &lt;span class="nt"&gt;--minify&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.clusters[0].cluster.server}'&lt;/span&gt;

&lt;span class="c"&gt;# Custom table: pod name, QoS class, and CPU limit&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;-o&lt;/span&gt; custom-columns&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s1"&gt;'NAME:.metadata.name,QOS:.status.qosClass,CPU-LIMIT:.spec.containers[0].resources.limits.cpu'&lt;/span&gt;

&lt;span class="c"&gt;# Pipe to jq for complex queries&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;-o&lt;/span&gt; json | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="s1"&gt;'.items[] | select(.status.phase=="Running") | .metadata.name'&lt;/span&gt;

&lt;span class="c"&gt;# Find all images across all namespaces (deduplicated)&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{range .items[*]}{.spec.containers[*].image}{"\n"}{end}'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;

&lt;span class="c"&gt;# Use -o name to feed into another command&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;api-gateway &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;-o&lt;/span&gt; name | &lt;span class="se"&gt;\&lt;/span&gt;
  xargs &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt; kubectl annotate &lt;span class="o"&gt;{}&lt;/span&gt; debug-session&lt;span class="o"&gt;=&lt;/span&gt;2026-04-27 &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10 — kubectl proxy: Accessing the API Directly
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;kubectl proxy&lt;/code&gt; starts a local HTTP server that proxies authenticated requests to the Kubernetes API server. It's different from &lt;code&gt;port-forward&lt;/code&gt;: proxy gives you access to the Kubernetes REST API itself, not to a specific service inside the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a proxy on localhost:8001 (default)&lt;/span&gt;
kubectl proxy

&lt;span class="c"&gt;# Now you can curl the API without any auth headers&lt;/span&gt;
curl http://localhost:8001/api/v1/namespaces/production/pods

&lt;span class="c"&gt;# Access cluster metrics via the metrics API&lt;/span&gt;
curl http://localhost:8001/apis/metrics.k8s.io/v1beta1/nodes

&lt;span class="c"&gt;# Access a service through the API proxy&lt;/span&gt;
&lt;span class="c"&gt;# Format: /api/v1/namespaces/{ns}/services/{scheme:name:port}/proxy/&lt;/span&gt;
curl http://localhost:8001/api/v1/namespaces/monitoring/services/http:grafana:3000/proxy/

&lt;span class="c"&gt;# Proxy on a specific port&lt;/span&gt;
kubectl proxy &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9090

&lt;span class="c"&gt;# Accept connections from all interfaces (remote dev environments)&lt;/span&gt;
kubectl proxy &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 &lt;span class="nt"&gt;--accept-hosts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'.*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service proxy URL pattern is especially useful for accessing cluster-internal dashboards (Grafana, Prometheus, Argo CD) without setting up ingress or port-forward sessions. Keep the proxy running in a background terminal and bookmark the URLs.&lt;/p&gt;




&lt;h2&gt;
  
  
  11 — The Plugin Ecosystem: What krew Unlocks
&lt;/h2&gt;

&lt;p&gt;kubectl's plugin system lets you drop any executable named &lt;code&gt;kubectl-something&lt;/code&gt; onto your PATH and run it as &lt;code&gt;kubectl something&lt;/code&gt;. &lt;a href="https://krew.sigs.k8s.io" rel="noopener noreferrer"&gt;krew&lt;/a&gt; is the community package manager for these plugins. Here are the ones that actually earn their place:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl neat&lt;/code&gt; — Strip managed fields from &lt;code&gt;get -o yaml&lt;/code&gt; output so it's actually readable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl images&lt;/code&gt; — List container images in a tree view&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl resource-capacity&lt;/code&gt; — Node capacity vs requests vs limits at a glance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl ns&lt;/code&gt; — Switch namespace quickly (kubens alternative)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl sniff&lt;/code&gt; — Capture pod traffic with tcpdump/Wireshark&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl mtail&lt;/code&gt; — Multi-pod log tail with regex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl who-can&lt;/code&gt; — Who has permission to do X?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl access-matrix&lt;/code&gt; — Full RBAC matrix for a namespace&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl popeye&lt;/code&gt; — Cluster configuration linter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Productivity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl ctx&lt;/code&gt; — Switch cluster context (kubectx alternative)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl tree&lt;/code&gt; — Show owner references as a tree&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl view-secret&lt;/code&gt; — Decode secrets without base64 manually
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install krew (macOS/Linux)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/x86_64/amd64/'&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\(arm\)\(64\)\?.*/\1\2/'&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/aarch64$/arm64/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"krew-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/kubernetes-sigs/krew/releases/latest/download/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nb"&gt;tar &lt;/span&gt;zxvf &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  ./&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;krew
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Install the ones worth having&lt;/span&gt;
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;neat who-can access-matrix resource-capacity view-secret tree images popeye sniff

&lt;span class="c"&gt;# neat: removes clutter from -o yaml output (managedFields, creationTimestamp, etc.)&lt;/span&gt;
kubectl get deploy api-gateway &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | kubectl neat

&lt;span class="c"&gt;# who-can: find out which subjects can perform an action&lt;/span&gt;
kubectl who-can delete pods &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# view-secret: decode and display a secret's values&lt;/span&gt;
kubectl view-secret db-creds &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# tree: visualise the ownership hierarchy&lt;/span&gt;
kubectl tree deploy api-gateway &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# resource-capacity: see requests/limits vs actual usage per node&lt;/span&gt;
kubectl resource-capacity &lt;span class="nt"&gt;--sort&lt;/span&gt; cpu.limit &lt;span class="nt"&gt;--pods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12 — Watching Resources: Smarter Than You Think
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-w&lt;/code&gt; flag on &lt;code&gt;kubectl get&lt;/code&gt; opens a watch stream against the API server. Most people use it for pods, but it works on any resource. Combined with field selectors and output formatting, it becomes a real-time monitoring tool without needing a dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Watch pods but only show ones that are NOT Running&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; Running

&lt;span class="c"&gt;# Watch events filtered to a specific pod&lt;/span&gt;
kubectl get events &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;--field-selector&lt;/span&gt; involvedObject.name&lt;span class="o"&gt;=&lt;/span&gt;api-pod-7d4f8b &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Watch a HPA during a load test&lt;/span&gt;
kubectl get hpa api-gateway &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Watch all deployments for rollout changes&lt;/span&gt;
kubectl get deployments &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Watch nodes during a cluster upgrade&lt;/span&gt;
kubectl get nodes &lt;span class="nt"&gt;-w&lt;/span&gt;

&lt;span class="c"&gt;# Watch with a custom column showing only name and ready status&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; custom-columns&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'NAME:.metadata.name,READY:.status.conditions[?(@.type=="Ready")].status'&lt;/span&gt;

&lt;span class="c"&gt;# Watch PVCs for bound state during StatefulSet provisioning&lt;/span&gt;
kubectl get pvc &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;kubectl is one of those tools where the ceiling keeps rising the longer you use it. The gap between someone who knows &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; and someone who reaches for &lt;code&gt;kubectl wait&lt;/code&gt; in a CI script, or &lt;code&gt;kubectl patch --type=json&lt;/code&gt; when they need precision surgery on a live resource, is real and measurable in minutes saved per incident.&lt;/p&gt;

&lt;p&gt;The pattern worth internalising: &lt;strong&gt;kubectl is just an HTTP client with great defaults and a thoughtful DSL.&lt;/strong&gt; Once that clicks, every command stops being a magic incantation and starts being a readable REST call. You start predicting flags you've never tried before. You stop reaching for the browser. And when something breaks at 2am, you move faster.&lt;/p&gt;

&lt;p&gt;Start with the shorthands. Add &lt;code&gt;kubectl explain&lt;/code&gt; to your muscle memory. Reach for &lt;code&gt;kubectl wait&lt;/code&gt; the next time you write a deploy script. The rest will follow naturally.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/" rel="noopener noreferrer"&gt;Kubernetes Documentation: kubectl CLI Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7396" rel="noopener noreferrer"&gt;RFC 7396: JSON Merge Patch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc6902" rel="noopener noreferrer"&gt;RFC 6902: JSON Patch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://krew.sigs.k8s.io" rel="noopener noreferrer"&gt;krew: kubectl Plugin Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/jsonpath/" rel="noopener noreferrer"&gt;Kubernetes JSONPath Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/" rel="noopener noreferrer"&gt;kubectl Strategic Merge Patch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/kubectl/quick-reference/" rel="noopener noreferrer"&gt;kubectl Cheat Sheet (Official)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Kubernetes v1.36 "Haru" overview</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Fri, 24 Apr 2026 18:12:09 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/kubernetes-v136-haru-overview-4g1l</link>
      <guid>https://forem.com/selmaguedidi/kubernetes-v136-haru-overview-4g1l</guid>
      <description>&lt;p&gt;&lt;strong&gt;Released April 22, 2026.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kubernetes v1.36, just released on April 22, 2026, brings a wide set of improvements that feel less like experimentation and more like completion. With around 70 enhancements across stable, beta, and alpha stages, this release is not about flashy new ideas. It is about finishing what has been in progress for years and making core features reliable enough for everyday production use.&lt;/p&gt;

&lt;p&gt;The release is named &lt;em&gt;&lt;strong&gt;Haru&lt;/strong&gt;&lt;/em&gt;, a sound in Japanese associated with spring and clarity. It is a fitting name. Many long-running efforts have finally reached maturity, especially in areas like security, resource management, and cluster operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  The security story is the headline
&lt;/h2&gt;

&lt;p&gt;Three separate security features reached &lt;em&gt;&lt;strong&gt;GA&lt;/strong&gt;&lt;/em&gt; (General Availability) in this release, and taken together they represent a meaningful shift in how Kubernetes handles isolation and privilege. None of them are new ideas, but all three are now stable and production-ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Namespaces&lt;/strong&gt; is the biggest one. The concept has been in progress since KEP #127 and it has been a long road. What it does is remap a container's internal root user to an unprivileged UID on the actual host. A process inside the container thinks it is root. The host disagrees. If that process breaks out of the container, it lands on the node with no privileges at all. This does not eliminate container escapes, but it takes the worst-case outcome from "attacker has root on your node" to "attacker has nothing." For multi-tenant clusters, that gap is everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fine-grained kubelet API authorization&lt;/strong&gt; is the other one that should have existed sooner. Previously, granting monitoring tools access to the kubelet's API meant handing over the &lt;code&gt;nodes/proxy&lt;/code&gt; permission, which is much broader than any metrics scraper actually needs. This feature enables precise, least-privilege access control over individual kubelet API endpoints. Monitoring and observability tools can now get exactly the access they need and nothing more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constrained impersonation&lt;/strong&gt; is in beta, not GA, but it deserves mention here because it closes a real security gap. Kubernetes impersonation has historically been all-or-nothing: if you have permission to impersonate an identity, you can do anything that identity can do. The constrained model splits this into two separate checks. You need permission to impersonate, and you separately need permission for the specific action you want to perform as that identity. Misconfigured RBAC no longer translates into accidental privilege escalation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Admission webhooks just got a serious competitor
&lt;/h2&gt;

&lt;p&gt;If you have run admission webhooks in production you know the operational tax. There is the webhook service to maintain, the TLS certificates to rotate, the latency added to every single API request, and the debugging experience when something goes wrong mid-deploy. It is not unmanageable but it is never free.&lt;/p&gt;

&lt;p&gt;Mutating Admission Policies reached stable in v1.36. You write mutation logic in CEL directly in the API server. No external service, no network hop, no certificate. For the majority of real-world use cases like enforcing labels, defaulting fields, injecting sidecars this covers the ground. The feature is stable, versioned, and backed by the full API machinery guarantees.&lt;/p&gt;

&lt;p&gt;This does not mean webhooks are going away. Complex mutations that require external state or multiple API calls still need a webhook. But if your webhooks are doing simple declarative work, the migration path is now clear and the destination is considerably less painful to operate.&lt;/p&gt;




&lt;h2&gt;
  
  
  PSI metrics give you a better view of what your nodes are actually doing
&lt;/h2&gt;

&lt;p&gt;Utilization metrics lie to you, slightly. A node at 70% CPU utilization can be perfectly healthy or completely saturated depending on whether processes are actually getting scheduled. Pressure Stall Information tells you what utilization cannot: how much time workloads are blocked waiting for CPU, memory, or I/O.&lt;/p&gt;

&lt;p&gt;The difference in practice is significant. With PSI you can distinguish between a node that is busy and a node that is stalling. Vertical autoscalers become more accurate. Resource requests become easier to tune. Noisy neighbor problems become visible before they cause actual incidents instead of after. This feature is now stable, and it requires cgroupv2 which should already be your baseline at this point.&lt;/p&gt;




&lt;h2&gt;
  
  
  OCI artifacts as volumes is quietly interesting for ML teams
&lt;/h2&gt;

&lt;p&gt;This one did not get top billing in the release notes but it has real implications for how ML inference workloads get built.&lt;/p&gt;

&lt;p&gt;You can now mount an OCI artifact directly into a pod as a volume. The kubelet pulls from any OCI-compliant registry and mounts the content. No PVC, no storage backend, no init container pulling files down at startup. If you already package your model weights or static assets as OCI images for versioning purposes, you can now deliver them to pods using the exact same registry infrastructure you use for container images.&lt;/p&gt;

&lt;p&gt;Content-addressable storage, immutable versioning, and familiar tooling, all for free, because you were already using a registry. Teams that are not yet doing this should at least be aware it is now an option.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gang scheduling is finally coming to core Kubernetes
&lt;/h2&gt;

&lt;p&gt;This has been an open gap for a long time. Distributed training jobs, simulation workloads, anything where a group of pods either all need to run together or it is not worth running any of them, vanilla Kubernetes has never had a good answer. You either used external schedulers like Volcano or you engineered around the limitation.&lt;/p&gt;

&lt;p&gt;The new PodGroup API, landing in alpha, treats a group of pods as a single scheduling unit. Either all pods in the group are bound to nodes, or none of them are. No more partial allocations where a job half-starts and then sits waiting for resources that may never arrive.&lt;/p&gt;

&lt;p&gt;It is alpha, so do not build production workflows on it today. But the design is sound and the direction is clear. Native gang scheduling in core Kubernetes is coming.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things worth knowing before you upgrade
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Node log query is GA.&lt;/strong&gt; You can now read kubelet and kube-proxy logs via &lt;code&gt;kubectl&lt;/code&gt; without SSH access to the node. The &lt;code&gt;NodeLogQuery&lt;/code&gt; feature gate is on by default. Small ergonomic improvement but during an incident where node access requires a separate approval flow, it matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;gitRepo&lt;/code&gt; volume plugin is permanently removed.&lt;/strong&gt; It was deprecated in v1.11 and has been gone in spirit for years, but until v1.36 it could still technically run. Now it cannot and there is no flag to bring it back. The plugin ran git as root on the node which is as bad as it sounds. Replace it with an init container running git-sync. If you have workloads still using &lt;code&gt;gitRepo&lt;/code&gt;, they will fail after this upgrade. Check before you upgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Service.spec.externalIPs&lt;/code&gt; is deprecated.&lt;/strong&gt; This field has been a known MITM attack vector since CVE-2020-8554. Full removal is planned for v1.43, so you have time, but start looking at LoadBalancer services or the Gateway API as replacements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mixed version proxy is in beta.&lt;/strong&gt; HA clusters doing rolling control plane upgrades have always had a window where different apiservers are serving different API versions, leading to sporadic 404s. This feature routes each request to whichever apiserver can actually serve it. Much safer upgrade windows for anyone running multiple apiserver replicas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutable resources for suspended jobs is beta and enabled by default.&lt;/strong&gt; You can suspend a Job, update its CPU, memory, or GPU requests, and resume it without destroying and recreating pods. If you are building queue-based batch scheduling systems this is the feature that makes dynamic resource adjustment actually clean to implement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HPA scale-to-zero for external metrics is in alpha.&lt;/strong&gt; The autoscaler can now bring a deployment down to zero replicas based on Object or External metrics like queue depth, and scale it back up when work arrives. Still behind a feature gate, but the infrastructure cost savings for idle queue-driven workloads are obvious.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full changelog
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Stable (GA) in v1.36
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;User Namespaces in pods&lt;/li&gt;
&lt;li&gt;Mutating Admission Policies&lt;/li&gt;
&lt;li&gt;External signing of ServiceAccount tokens&lt;/li&gt;
&lt;li&gt;Fine-grained kubelet API authorization&lt;/li&gt;
&lt;li&gt;Volume Group Snapshots&lt;/li&gt;
&lt;li&gt;Mutable CSINode Allocatable (volume attach limits)&lt;/li&gt;
&lt;li&gt;OCI Artifact volume source&lt;/li&gt;
&lt;li&gt;Node log query&lt;/li&gt;
&lt;li&gt;PSI metrics on cgroupv2&lt;/li&gt;
&lt;li&gt;SELinux volume labeling speedup&lt;/li&gt;
&lt;li&gt;DRA admin access for ResourceClaims and ResourceClaimTemplates&lt;/li&gt;
&lt;li&gt;DRA prioritized alternatives in device requests&lt;/li&gt;
&lt;li&gt;DRA extended PodResources for Dynamic Resource Allocation&lt;/li&gt;
&lt;li&gt;Declarative validation with validation-gen&lt;/li&gt;
&lt;li&gt;Removal of gogoprotobuf dependency for Kubernetes API types&lt;/li&gt;
&lt;li&gt;Portworx in-tree to CSI driver migration&lt;/li&gt;
&lt;li&gt;ProcMount option&lt;/li&gt;
&lt;li&gt;CSI driver opt-in for service account tokens via secrets field&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔵 Promoted to Beta in v1.36
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Constrained impersonation&lt;/li&gt;
&lt;li&gt;Strict IP/CIDR validation&lt;/li&gt;
&lt;li&gt;Mutable container resources for suspended Jobs&lt;/li&gt;
&lt;li&gt;Mixed version proxy&lt;/li&gt;
&lt;li&gt;DRA partitionable devices and consumable capacity&lt;/li&gt;
&lt;li&gt;DRA device taints and tolerations&lt;/li&gt;
&lt;li&gt;DRA ResourceClaim device status&lt;/li&gt;
&lt;li&gt;Memory QoS with cgroupv2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.kuberc&lt;/code&gt; user preferences for kubectl&lt;/li&gt;
&lt;li&gt;Staleness mitigation for controllers&lt;/li&gt;
&lt;li&gt;ComponentStatusz &lt;code&gt;/statusz&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;ComponentFlagz &lt;code&gt;/flagz&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;Resource health status via &lt;code&gt;allocatedResourcesStatus&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧪 New in Alpha in v1.36
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Workload Aware Scheduling with PodGroup API&lt;/li&gt;
&lt;li&gt;HPA scale-to-zero for Object and External metrics&lt;/li&gt;
&lt;li&gt;Native sparse histogram support for Kubernetes metrics&lt;/li&gt;
&lt;li&gt;Manifest-based admission control configuration&lt;/li&gt;
&lt;li&gt;CRI list streaming for the kubelet&lt;/li&gt;
&lt;li&gt;DRA native resources for CPU management&lt;/li&gt;
&lt;li&gt;DRA downward API for resource attributes&lt;/li&gt;
&lt;li&gt;DRA ResourceClaim support for workload controllers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚠️ Deprecations and Removals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Removed:&lt;/strong&gt; &lt;code&gt;gitRepo&lt;/code&gt; volume plugin, permanently and without a fallback option&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deprecated:&lt;/strong&gt; &lt;code&gt;Service.spec.externalIPs&lt;/code&gt;, planned removal in v1.43&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;v1.36 is a maturation release more than a revolution. The headline features are things the community has been waiting on for a while (user namespaces, mutating admission policies, PSI metrics) and watching them land at GA feels less like news and more like relief. The alpha features, particularly gang scheduling and HPA scale-to-zero, sketch out clearly where the next few releases are going for AI and batch workloads.&lt;/p&gt;

&lt;p&gt;The practical upgrade advice is simple: audit for &lt;code&gt;gitRepo&lt;/code&gt; usage before anything else. That is the one thing in this release that will break silently if you are not looking for it. Everything else is additive or behind feature gates.&lt;/p&gt;

&lt;p&gt;The Kubernetes project has been shipping at a remarkable cadence for over a decade. v1.36 is another steady step in the same direction. &lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/blog/2025/04/23/kubernetes-v1-36-release/" rel="noopener noreferrer"&gt;Kubernetes v1.36 Official Release Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md" rel="noopener noreferrer"&gt;Full v1.36 Release Notes on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/127" rel="noopener noreferrer"&gt;KEP #127: User Namespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/3962" rel="noopener noreferrer"&gt;KEP #3962: Mutating Admission Policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/4639" rel="noopener noreferrer"&gt;KEP #4639: OCI Artifact Volume Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/4205" rel="noopener noreferrer"&gt;KEP #4205: PSI Metrics on cgroupv2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/4671" rel="noopener noreferrer"&gt;KEP #4671: Workload Aware Scheduling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/5040" rel="noopener noreferrer"&gt;KEP #5040: Remove gitRepo Volume Driver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/enhancements/issues/5707" rel="noopener noreferrer"&gt;KEP #5707: Deprecate Service externalIPs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/using-api/deprecation-policy/" rel="noopener noreferrer"&gt;Kubernetes Deprecation and Removal Policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>containers</category>
      <category>docker</category>
      <category>security</category>
    </item>
    <item>
      <title>Publishing Your Java Library to Maven Central</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Sun, 05 Oct 2025 18:37:55 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/publishing-your-java-library-to-maven-central-15in</link>
      <guid>https://forem.com/selmaguedidi/publishing-your-java-library-to-maven-central-15in</guid>
      <description>&lt;h2&gt;
  
  
  Why Maven Central Matters
&lt;/h2&gt;

&lt;p&gt;You've built an amazing Java library. It solves real problems, the code is clean, and you're ready to share it with the world. But there's one crucial step between your local repository and global adoption: publishing to Maven Central.&lt;/p&gt;

&lt;p&gt;Maven Central isn't just another package repository: it's the canonical repository for Java and JVM libraries. With over 10 million artifacts and billions of downloads monthly, it's where the entire Java ecosystem comes to find dependencies. When your library is on Maven Central, developers can add it to their projects with a single dependency declaration. No custom repositories, no manual downloads, no friction.&lt;/p&gt;

&lt;p&gt;In this guide, I'll walk you through the entire process of publishing to Maven Central, from initial setup to your first release. Whether you're publishing your first library or your tenth, this guide will help you navigate the journey.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create Your Sonatype Account&lt;/strong&gt;&lt;br&gt;
Maven Central is operated by Sonatype, and they've recently modernized the onboarding process. You'll need to create an account on their new platform.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://central.sonatype.com" rel="noopener noreferrer"&gt;https://central.sonatype.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "Sign up" (you can use GitHub, Google, or email)&lt;/li&gt;
&lt;li&gt;Verify your email address if using email registration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Claim Your Namespace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Namespace ownership is fundamental to publishing on Maven Central. Your namespace (groupId) serves as the unique identifier for all your published artifacts, and you must demonstrate legitimate ownership before you can publish. The Central Portal offers two primary verification methods, each suited to different use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 1: DNS-Based Verification (For Domain Owners)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you own or maintain a registered domain name, you can leverage the Domain Name System in reversed format for your namespace. This approach mirrors Java's package naming convention and provides the most flexibility for organizational publishing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How It Works:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The namespace uses your domain name in reverse order, allowing you to create as many subsections as needed for organizational purposes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Examples:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain: example.com → Namespace: com.example&lt;/li&gt;
&lt;li&gt;Domain: subdomain.example.com → Namespace: com.example&lt;/li&gt;
&lt;li&gt;Domain: my-domain.com → Namespace: com.my-domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once verified, you can utilize any groupId starting with your reversed domain and extend it with additional subsections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;com.example.domain&lt;/li&gt;
&lt;li&gt;com.example.test&lt;/li&gt;
&lt;li&gt;com.example.utilities.logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Important Considerations:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Exact Reversal: The groupId must reverse the domain name exactly as it appears, preserving hyphens and special characters even if they would produce invalid Java package names. This is acceptable and expected, your Java package names need not match your groupId.&lt;br&gt;
Ownership Requirement: You must have control over the domain to add DNS verification records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification Process:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to "Namespaces" in the left sidebar&lt;/li&gt;
&lt;li&gt;Click "Add Namespace"&lt;/li&gt;
&lt;li&gt;Enter your reversed domain namespace (e.g., com.example)&lt;/li&gt;
&lt;li&gt;Select "DNS Verification" as your verification method&lt;/li&gt;
&lt;li&gt;Copy the provided TXT record from the portal&lt;/li&gt;
&lt;li&gt;Add this TXT record to your domain's DNS configuration&lt;/li&gt;
&lt;li&gt;Return to the portal and click "Verify Namespace"&lt;/li&gt;
&lt;li&gt;Verification typically completes within minutes once DNS propagates&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Method 2: Code Hosting Service Verification (Personal Namespace)
&lt;/h3&gt;

&lt;p&gt;For individual developers and open-source projects hosted on popular code hosting platforms, Maven Central provides a streamlined verification process using your platform identity. This method is ideal for personal libraries and projects without a custom domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supported Platforms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code Hosting Service&lt;/th&gt;
&lt;th&gt;Username Pattern&lt;/th&gt;
&lt;th&gt;Example Namespace&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myusername&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.github.myusername&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myusername&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.gitlab.myusername&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gitee&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myusername&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.gitee.myusername&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitbucket&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myusername&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;io.bitbucket.myusername&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The namespace format follows the pattern &lt;code&gt;io.{platform}.{username}&lt;/code&gt;, where the username matches your account on the respective platform. This creates a globally unique namespace tied to your platform identity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification Process:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to "Namespaces"&lt;/li&gt;
&lt;li&gt;Click "Add Namespace"&lt;/li&gt;
&lt;li&gt;Enter your platform-based namespace (e.g., &lt;code&gt;io.github.myusername&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Select your code hosting service as the verification method&lt;/li&gt;
&lt;li&gt;The portal will prompt you to create a temporary public repository with a specific verification name &lt;/li&gt;
&lt;li&gt;Create the repository on your platform with the exact name provided&lt;/li&gt;
&lt;li&gt;Return to the portal and click "Verify Namespace"&lt;/li&gt;
&lt;li&gt;Verification is typically instant once the repository is detected&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Step 3: Generate GPG Keys
&lt;/h2&gt;

&lt;p&gt;Maven Central enforces a strict security requirement: all published artifacts must be cryptographically signed using GPG (GNU Privacy Guard). This digital signature ensures artifact integrity and authenticity, protecting the entire Java ecosystem from tampering and unauthorized modifications.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.1 Installing GPG
&lt;/h3&gt;

&lt;p&gt;Before generating keys, you need to ensure GPG is installed on your system.&lt;/p&gt;
&lt;h4&gt;
  
  
  For macOS
&lt;/h4&gt;

&lt;p&gt;Using Homebrew (recommended):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using MacPorts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;port &lt;span class="nb"&gt;install &lt;/span&gt;gnupg2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  For Linux
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Debian/Ubuntu:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fedora/RHEL/CentOS:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arch Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  For Windows
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Using Gpg4win (Recommended)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download Gpg4win from &lt;a href="https://gpg4win.org/download.html" rel="noopener noreferrer"&gt;https://gpg4win.org/download.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run the installer and follow the installation wizard&lt;/li&gt;
&lt;li&gt;Add GPG to your PATH if not done automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Option 2: Using Chocolatey&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;choco &lt;span class="nb"&gt;install &lt;/span&gt;gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 3: Using Windows Subsystem for Linux (WSL)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Verify installation (in Command Prompt or PowerShell):&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Generating Your Key Pair
&lt;/h3&gt;

&lt;p&gt;Once GPG is installed, generate a new key pair for signing your artifacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--gen-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the interactive prompts to configure your key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key type&lt;/strong&gt;: Choose RSA and RSA (default)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key size&lt;/strong&gt;: Use 2048 bits or higher (4096 recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration&lt;/strong&gt;: Set to 2-3 years (recommended) or no expiration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real name&lt;/strong&gt;: Your name or organization name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email address&lt;/strong&gt;: A professional email you control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comment&lt;/strong&gt;: Optional (e.g., "Maven Central Signing Key")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase&lt;/strong&gt;: Create a strong passphrase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical&lt;/strong&gt;: Your passphrase will be required for every release. Store it securely in a password manager—you cannot recover it if lost, and you'll need it throughout your publishing workflow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Listing Your Keys
&lt;/h4&gt;

&lt;p&gt;After generation, verify your keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--list-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub   rsa4096 2025-10-05 [SC] [expires: 2027-10-05]
      1234567890ABCDEF1234567890ABCDEF12345678
uid           [ultimate] John Doe (Maven Central Signing Key) &amp;lt;john.doe@example.com&amp;gt;
sub   rsa4096 2025-10-05 [E] [expires: 2027-10-05]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The long hexadecimal string is your &lt;strong&gt;key ID&lt;/strong&gt;. You can also use the last 8 characters as a short key ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Publishing Your Public Key
&lt;/h3&gt;

&lt;p&gt;For Maven Central to verify your signatures, your public key must be published to a public key server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using the full key ID&lt;/span&gt;
gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; keys.openpgp.org &lt;span class="nt"&gt;--send-keys&lt;/span&gt; 1234567890ABCDEF1234567890ABCDEF12345678

&lt;span class="c"&gt;# Or using the short key ID (last 8 characters)&lt;/span&gt;
gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; keys.openpgp.org &lt;span class="nt"&gt;--send-keys&lt;/span&gt; 12345678
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It may take a few minutes for your key to propagate across key server networks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verifying Key Publication
&lt;/h4&gt;

&lt;p&gt;Confirm your key is publicly accessible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; keys.openpgp.org &lt;span class="nt"&gt;--recv-keys&lt;/span&gt; YOUR_KEY_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If successful, you'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg: key YOUR_KEY_ID: "John Doe (Maven Central Signing Key) &amp;lt;john.doe@example.com&amp;gt;" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;p&gt;With your GPG keys generated, published, and backed up, you're ready to configure your build tool to automatically sign artifacts during the publishing process. Your key ID and passphrase will be required in the build configuration covered in Step 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Configure Your Build Tool
&lt;/h2&gt;

&lt;p&gt;Configuring Maven for publishing to Maven Central requires understanding two distinct deployment workflows: &lt;strong&gt;release versions&lt;/strong&gt; and &lt;strong&gt;snapshot versions&lt;/strong&gt;. Each serves a different purpose in the development lifecycle and has different requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Release vs Snapshot Versions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Release Versions&lt;/strong&gt; (e.g., &lt;code&gt;1.0.0&lt;/code&gt;, &lt;code&gt;2.3.1&lt;/code&gt;, &lt;code&gt;1.5.0-beta1&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immutable and permanent once published&lt;/li&gt;
&lt;li&gt;Require complete metadata, including developers, SCM, and license information&lt;/li&gt;
&lt;li&gt;Undergo full validation before publication&lt;/li&gt;
&lt;li&gt;Intended for production use and stable APIs&lt;/li&gt;
&lt;li&gt;Cannot be overwritten or deleted after publication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Snapshot Versions&lt;/strong&gt; (e.g., &lt;code&gt;1.0.0-SNAPSHOT&lt;/code&gt;, &lt;code&gt;2.3.1-SNAPSHOT&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutable and can be republished with the same version&lt;/li&gt;
&lt;li&gt;Require minimal metadata validation&lt;/li&gt;
&lt;li&gt;Used for development, testing, and continuous integration&lt;/li&gt;
&lt;li&gt;Not indexed in Maven Central search&lt;/li&gt;
&lt;li&gt;Must be explicitly enabled in your namespace configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generating Authentication Tokens
&lt;/h3&gt;

&lt;p&gt;Before configuring your build, you need to generate authentication tokens from the Central Portal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://central.sonatype.com" rel="noopener noreferrer"&gt;https://central.sonatype.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click on your account name in the top right corner&lt;/li&gt;
&lt;li&gt;Select "View User Tokens" from the dropdown&lt;/li&gt;
&lt;li&gt;Click "Generate User Token"&lt;/li&gt;
&lt;li&gt;The portal will display your username and password token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save these immediately&lt;/strong&gt;—the password token is only shown once&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: These tokens are not your Sonatype login credentials. They are specifically generated for programmatic publishing and should be treated as API keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling Snapshot Publishing (Optional)
&lt;/h3&gt;

&lt;p&gt;If you plan to publish snapshot versions, you must enable this feature in your namespace:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://central.sonatype.com" rel="noopener noreferrer"&gt;https://central.sonatype.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to "Namespaces"&lt;/li&gt;
&lt;li&gt;Select your verified namespace&lt;/li&gt;
&lt;li&gt;Click on its "dropdown menu"&lt;/li&gt;
&lt;li&gt;Click on "Enable Snapshots"&lt;/li&gt;
&lt;li&gt;Save the configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without this setting enabled, snapshot deployments will fail with an authorization error.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Configuration for Release Versions
&lt;/h2&gt;

&lt;p&gt;Release versions require comprehensive metadata and validation. This configuration ensures your artifacts meet all Maven Central requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maven POM Configuration
&lt;/h3&gt;

&lt;p&gt;Add the following to your &lt;code&gt;pom.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;project&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.yourusername&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;your-library&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;packaging&amp;gt;&lt;/span&gt;jar&lt;span class="nt"&gt;&amp;lt;/packaging&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Your Library Name&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;A comprehensive description of your library's purpose and functionality&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://github.com/yourusername/your-library&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;licenses&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;license&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Apache License, Version 2.0&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://www.apache.org/licenses/LICENSE-2.0.txt&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;distribution&amp;gt;&lt;/span&gt;repo&lt;span class="nt"&gt;&amp;lt;/distribution&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/license&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/licenses&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;developers&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;developer&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;yourusername&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Your Full Name&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;email&amp;gt;&lt;/span&gt;your.email@example.com&lt;span class="nt"&gt;&amp;lt;/email&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;organization&amp;gt;&lt;/span&gt;Your Organization&lt;span class="nt"&gt;&amp;lt;/organization&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;organizationUrl&amp;gt;&lt;/span&gt;https://yoursite.com&lt;span class="nt"&gt;&amp;lt;/organizationUrl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/developer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/developers&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;scm&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;connection&amp;gt;&lt;/span&gt;scm:git:git://github.com/yourusername/your-library.git&lt;span class="nt"&gt;&amp;lt;/connection&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;developerConnection&amp;gt;&lt;/span&gt;scm:git:ssh://github.com:yourusername/your-library.git&lt;span class="nt"&gt;&amp;lt;/developerConnection&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://github.com/yourusername/your-library/tree/main&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tag&amp;gt;&lt;/span&gt;HEAD&lt;span class="nt"&gt;&amp;lt;/tag&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/scm&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- Central Publishing Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.sonatype.central&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;central-publishing-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.9.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;publishingServerId&amp;gt;&lt;/span&gt;central&lt;span class="nt"&gt;&amp;lt;/publishingServerId&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;autoPublish&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/autoPublish&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;waitUntil&amp;gt;&lt;/span&gt;published&lt;span class="nt"&gt;&amp;lt;/waitUntil&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- GPG Signing Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-gpg-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;sign-artifacts&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;sign&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;&amp;lt;!-- Retrieves passphrase from settings.xml --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;passphraseServerId&amp;gt;&lt;/span&gt;gpg.passphrase&lt;span class="nt"&gt;&amp;lt;/passphraseServerId&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;&amp;lt;!-- Required for gpg2 to avoid GUI prompts --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;gpgArguments&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;--pinentry-mode&lt;span class="nt"&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;loopback&lt;span class="nt"&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/gpgArguments&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;&amp;lt;!-- Ensure correct GPG binary is used --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;executable&amp;gt;&lt;/span&gt;gpg&lt;span class="nt"&gt;&amp;lt;/executable&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Source Attachment Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-source-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.3.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;attach-sources&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;jar-no-fork&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Javadoc Generation Plugin --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-javadoc-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.6.3&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;attach-javadocs&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;jar&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="c"&gt;&amp;lt;!-- Optional: Configure Javadoc strictness --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;doclint&amp;gt;&lt;/span&gt;none&lt;span class="nt"&gt;&amp;lt;/doclint&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;quiet&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/quiet&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugin Explanations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Central Publishing Maven Plugin
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.sonatype.central&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;central-publishing-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.9.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the modern publishing plugin that replaces the legacy Nexus staging plugin. It handles the entire deployment workflow to Maven Central.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Configuration Options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;publishingServerId&lt;/code&gt;: References the server credentials in &lt;code&gt;settings.xml&lt;/code&gt; (must be &lt;code&gt;central&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;autoPublish&lt;/code&gt;: When &lt;code&gt;true&lt;/code&gt;, automatically publishes after successful validation. When &lt;code&gt;false&lt;/code&gt;, requires manual approval in the Central Portal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;waitUntil&lt;/code&gt;: Controls deployment behavior

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uploaded&lt;/code&gt;: Waits until artifacts are uploaded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;validated&lt;/code&gt;: Waits until validation completes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;published&lt;/code&gt;: Waits until artifacts are fully published to Maven Central (recommended)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Maven GPG Plugin
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-gpg-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signs all project artifacts (JAR, sources, javadoc, POM) with your GPG key during the verify phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Configuration Options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;passphraseServerId&lt;/code&gt;: References the GPG passphrase stored in &lt;code&gt;settings.xml&lt;/code&gt;, avoiding hardcoded credentials&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gpgArguments&lt;/code&gt;: Configures GPG behavior

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--pinentry-mode loopback&lt;/code&gt;: Essential for non-interactive environments (CI/CD) and gpg2, prevents GUI password prompts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;executable&lt;/code&gt;: Specifies the GPG binary path. Use &lt;code&gt;gpg2&lt;/code&gt; if you have both versions installed&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Maven Source Plugin
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-source-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.3.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generates a JAR file containing your source code. Maven Central mandates source JARs for all releases.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jar-no-fork&lt;/code&gt;: Creates the source JAR without forking a new Maven lifecycle, improving build performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Maven Javadoc Plugin
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-javadoc-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.6.3&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generates comprehensive API documentation and packages it as a JAR. Maven Central requires Javadoc JARs for all releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;doclint&lt;/code&gt;: Set to &lt;code&gt;none&lt;/code&gt; to disable strict Javadoc validation (useful if you have legacy code with incomplete docs)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quiet&lt;/code&gt;: Reduces console output during generation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Maven Settings Configuration
&lt;/h3&gt;

&lt;p&gt;Add the following to your &lt;code&gt;~/.m2/settings.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;settings&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;servers&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Central Portal Authentication --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;server&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;central&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;username&amp;gt;&lt;/span&gt;YOUR_GENERATED_TOKEN_USERNAME&lt;span class="nt"&gt;&amp;lt;/username&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;YOUR_GENERATED_TOKEN_PASSWORD&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/server&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- GPG Passphrase --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;server&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;gpg.passphrase&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;passphrase&amp;gt;&lt;/span&gt;YOUR_GPG_PASSPHRASE&lt;span class="nt"&gt;&amp;lt;/passphrase&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/server&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/servers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploying a Release
&lt;/h3&gt;

&lt;p&gt;Execute the deployment with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn clean deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Clean previous build artifacts&lt;/li&gt;
&lt;li&gt;Compile your code&lt;/li&gt;
&lt;li&gt;Run tests&lt;/li&gt;
&lt;li&gt;Generate source and Javadoc JARs&lt;/li&gt;
&lt;li&gt;Sign all artifacts with GPG&lt;/li&gt;
&lt;li&gt;Upload to Maven Central&lt;/li&gt;
&lt;li&gt;Validate the deployment&lt;/li&gt;
&lt;li&gt;Automatically publish (if &lt;code&gt;autoPublish&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verification&lt;/strong&gt;: Monitor the deployment at &lt;a href="https://central.sonatype.com/publishing/deployments" rel="noopener noreferrer"&gt;https://central.sonatype.com/publishing/deployments&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuration for Snapshot Versions
&lt;/h2&gt;

&lt;p&gt;Snapshot versions are designed for rapid iteration during development. They have relaxed validation requirements and can be republished multiple times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maven POM Configuration for Snapshots
&lt;/h3&gt;

&lt;p&gt;Snapshots require minimal metadata compared to releases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;project&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.yourusername&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;your-library&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0-SNAPSHOT&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;packaging&amp;gt;&lt;/span&gt;jar&lt;span class="nt"&gt;&amp;lt;/packaging&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Minimal required metadata for snapshots --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Your Library Name&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Development snapshot of Your Library&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://github.com/yourusername/your-library&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- Central Publishing Plugin (same as releases) --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.sonatype.central&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;central-publishing-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.9.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;publishingServerId&amp;gt;&lt;/span&gt;central&lt;span class="nt"&gt;&amp;lt;/publishingServerId&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;autoPublish&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/autoPublish&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- GPG Signing (same as releases) --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-gpg-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;sign-artifacts&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;sign&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;passphraseServerId&amp;gt;&lt;/span&gt;gpg.passphrase&lt;span class="nt"&gt;&amp;lt;/passphraseServerId&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;gpgArguments&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;--pinentry-mode&lt;span class="nt"&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;loopback&lt;span class="nt"&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/gpgArguments&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;executable&amp;gt;&lt;/span&gt;gpg&lt;span class="nt"&gt;&amp;lt;/executable&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;

            &lt;span class="c"&gt;&amp;lt;!-- Source and Javadoc plugins are optional for snapshots --&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- Include them if you want consistent artifact structure --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Differences for Snapshots
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Relaxed Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;&amp;lt;developers&amp;gt;&lt;/code&gt; section required&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;&amp;lt;scm&amp;gt;&lt;/code&gt; section required&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;&amp;lt;licenses&amp;gt;&lt;/code&gt; section required (though recommended)&lt;/li&gt;
&lt;li&gt;Source and Javadoc JARs are optional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version Format:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Must end with &lt;code&gt;-SNAPSHOT&lt;/code&gt; (e.g., &lt;code&gt;1.0.0-SNAPSHOT&lt;/code&gt;, &lt;code&gt;2.1.0-SNAPSHOT&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Can be republished multiple times without changing the version number&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Publication Behavior:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not indexed in Maven Central search&lt;/li&gt;
&lt;li&gt;Available immediately after upload&lt;/li&gt;
&lt;li&gt;Stored in a separate snapshot repository&lt;/li&gt;
&lt;li&gt;Automatically cleaned up after a retention period&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploying a Snapshot
&lt;/h3&gt;

&lt;p&gt;Deploy snapshots using the same command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn clean deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maven automatically detects the &lt;code&gt;-SNAPSHOT&lt;/code&gt; suffix and routes the deployment to the snapshot repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consuming Published Snapshots
&lt;/h3&gt;

&lt;p&gt;To use snapshots published by others (or your own), configure your project's &lt;code&gt;pom.xml&lt;/code&gt; or &lt;code&gt;settings.xml&lt;/code&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  In Your Project's POM
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;central-portal-snapshots&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Central Portal Snapshots&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://central.sonatype.com/repository/maven-snapshots/&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;updatePolicy&amp;gt;&lt;/span&gt;always&lt;span class="nt"&gt;&amp;lt;/updatePolicy&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;releases&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/releases&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  In Your Settings.xml (Global Configuration)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;settings&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;central-snapshots&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;central-portal-snapshots&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Central Portal Snapshots&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://central.sonatype.com/repository/maven-snapshots/&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;snapshots&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;updatePolicy&amp;gt;&lt;/span&gt;always&lt;span class="nt"&gt;&amp;lt;/updatePolicy&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/snapshots&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;activeProfiles&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;activeProfile&amp;gt;&lt;/span&gt;central-snapshots&lt;span class="nt"&gt;&amp;lt;/activeProfile&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/activeProfiles&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuration Options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;updatePolicy&lt;/code&gt;: Controls how often Maven checks for snapshot updates

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;always&lt;/code&gt;: Check on every build (recommended for active development)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;daily&lt;/code&gt;: Check once per day (default)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;interval:X&lt;/code&gt;: Check every X minutes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt;: Never check for updates&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;For more detailed information and the latest updates, refer to the official Sonatype documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Central Portal Documentation&lt;/strong&gt;: &lt;a href="https://central.sonatype.org/register/central-portal/" rel="noopener noreferrer"&gt;https://central.sonatype.org/register/central-portal/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publishing Guide&lt;/strong&gt;: &lt;a href="https://central.sonatype.org/publish/publish-guide/" rel="noopener noreferrer"&gt;https://central.sonatype.org/publish/publish-guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maven Configuration&lt;/strong&gt;: &lt;a href="https://central.sonatype.org/publish/publish-maven/" rel="noopener noreferrer"&gt;https://central.sonatype.org/publish/publish-maven/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official documentation provides comprehensive coverage of edge cases, advanced configurations, and troubleshooting scenarios that may not be covered in this guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Sonatype recently migrated from the old JIRA-based system to the new Central Portal at central.sonatype.com. If you find older tutorials mentioning JIRA tickets and issues.sonatype.org, that process is now deprecated. This guide reflects the current, streamlined process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions or issues?&lt;/strong&gt; Drop a comment below, and I'll do my best to help!&lt;/p&gt;

</description>
      <category>java</category>
      <category>maven</category>
    </item>
    <item>
      <title>Understanding GitHub Webhooks: Leveraging Reverse Proxy with Ngrok for Local Development</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Thu, 25 Jul 2024 00:10:33 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/understanding-github-webhooks-leveraging-reverse-proxy-with-ngrok-for-local-development-2k7n</link>
      <guid>https://forem.com/selmaguedidi/understanding-github-webhooks-leveraging-reverse-proxy-with-ngrok-for-local-development-2k7n</guid>
      <description>&lt;p&gt;Webhooks are a powerful and flexible way for applications to communicate with each other in real-time. They are a crucial feature for developers who want to automate workflows and integrate with other services. In this blog post, we will explore what GitHub webhooks are, why we use them, and how to leverage a reverse proxy like &lt;em&gt;Ngrok&lt;/em&gt; to trigger events on a local development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are GitHub Webhooks?
&lt;/h2&gt;

&lt;p&gt;Webhooks allow you to subscribe to specific events occurring within a software system and automatically receive detailed data delivered to your server in real-time whenever those events take place. &lt;/p&gt;

&lt;p&gt;When you create a webhook, you specify a URL and subscribe to specific events that occur within your GitHub repository. These events can range from code pushes and pull requests to issues and comments. Whenever an event that your webhook is subscribed to occurs, GitHub sends an HTTP POST request with detailed data about the event to the URL you specified.&lt;/p&gt;

&lt;p&gt;The webhook's payload URL is the endpoint that will receive these HTTP requests. Your server must be set up to listen for webhook deliveries at this URL. When it receives a request, it can parse the event data and take appropriate actions. This allows for a wide variety of automated workflows, such as triggering CI/CD pipelines, sending notifications, or updating external systems with the latest repository information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detailed Workflow of GitHub Webhooks
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event Subscription:&lt;/strong&gt; When configuring a webhook, you choose which events your webhook should listen for. These could include events like push, pull_request, issues, release, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event Trigger:&lt;/strong&gt; When one of the subscribed events occurs in the repository, GitHub prepares an HTTP POST request with a payload containing details about the event. For example, a push event payload includes information about the commits, the branch involved, and the repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP Request to Payload URL:&lt;/strong&gt; GitHub sends the HTTP POST request to the specified payload URL. This request includes the payload data as JSON, providing all the necessary details about the event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Processing:&lt;/strong&gt; Your server, which is set up to handle requests at the payload URL, receives the webhook request. It then processes the payload data to determine what action to take. For instance, it might trigger a Jenkins build, post a message to a Slack channel, or update a database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Response to GitHub:&lt;/strong&gt; After processing the event, your server responds to GitHub with an HTTP status code. A &lt;code&gt;2xx&lt;/code&gt; status code indicates that the request was successfully received and processed. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Using Ngrok as a Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;In a local development environment, your server might not be accessible from the internet, making it difficult to test webhooks that require a publicly accessible endpoint. This is where Ngrok comes in handy. Ngrok is a tool that creates a secure tunnel to your local server, providing a public URL that GitHub can send webhooks to.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Ngrok Works?
&lt;/h2&gt;

&lt;p&gt;Ngrok establishes a secure connection between your local machine and a publicly accessible URL, effectively exposing your local server to the internet. Here’s a detailed breakdown of the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account in &lt;a href="//www.ngrok.com"&gt;ngrok.com&lt;/a&gt;:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Follow given instructions to install ngrok and add authtoken:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Example: Setting Up Ngrok to Trigger a Simple CI/CD Pipeline in Jenkins
&lt;/h2&gt;

&lt;p&gt;Let's walk through an example of setting up Ngrok to expose a local Jenkins server and configure a GitHub webhook to trigger a simple CI/CD pipeline when code is pushed to a repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Ensure Jenkins is Running&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this example, let's assume that your Jenkins server is already installed and running on port &lt;code&gt;8080&lt;/code&gt;. If Jenkins is running correctly, you should be able to access it in your browser at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Install Github plugin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To allow Jenkins to be triggered by GitHub webhooks, you need to install the GitHub plugin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;"Manage Jenkins” -&amp;gt; “Plugins”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Install the &lt;strong&gt;GitHub Plugin&lt;/strong&gt; and restart Jenkins if required.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3: Create a Jenkins Job&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click on “New Item,” enter a name for your job, select “Freestyle project,” and click “OK.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add git repository and specify branch(s)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the “GitHub hook trigger for GITScm polling” option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure your job. For simplicity, you might just echo a message in the Build section.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;strong&gt;Step 4: Start Ngrok&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open a terminal and start Ngrok to create a secure tunnel to your local Jenkins server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ngrok will provide a public URL, such as:&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 5: Configure GitHub Webhook&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your GitHub repository, configure a webhook to send events to your Jenkins server through the Ngrok URL:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub repository and go to “Settings” -&amp;gt; “Webhooks”&lt;/li&gt;
&lt;li&gt;Click “Add webhook”&lt;/li&gt;
&lt;li&gt;In the “Payload URL” field, enter the Ngrok URL followed by &lt;code&gt;/github-webhook/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click “Add webhook” to save.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your Jenkins server denies anonymous user access, you need to include authentication credentials in the webhook URL to avoid a 401 Unauthorized error. The URL format should be &lt;code&gt;https://user:password@PUBLIC_URL/github-webhook/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Test the Setup&lt;/strong&gt;&lt;br&gt;
To test the webhook setup, push a change to your GitHub repository. This should trigger the webhook and send a request to your Jenkins server via the Ngrok tunnel. Jenkins will then trigger the configured job.&lt;/p&gt;

&lt;p&gt;You can verify this by checking the Jenkins job console output. You should see the echo message "Build triggered by GitHub webhook," indicating that the webhook successfully triggered the CI/CD pipeline.&lt;/p&gt;

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

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

&lt;p&gt;GitHub webhooks enable real-time automation by notifying external services about repository events. Testing these integrations locally can be challenging due to accessibility constraints. Ngrok simplifies this by creating a secure tunnel to your local server, allowing you to test webhook configurations as if they were exposed on the internet.&lt;/p&gt;

&lt;p&gt;In our example, we demonstrated how to use Ngrok to trigger a Jenkins CI/CD pipeline from GitHub events. This setup allows for seamless testing of webhooks in a local environment, ensuring your integrations work as expected before deployment. Overall, leveraging GitHub webhooks with Ngrok enhances development efficiency and reliability.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>jenkins</category>
      <category>ngrok</category>
      <category>devops</category>
    </item>
    <item>
      <title>A dive into Jenkins Configuration as Code (JCasC)</title>
      <dc:creator>Selma Guedidi</dc:creator>
      <pubDate>Mon, 22 Jul 2024 19:05:06 +0000</pubDate>
      <link>https://forem.com/selmaguedidi/a-dive-into-jenkins-configuration-as-code-jcasc-4dj0</link>
      <guid>https://forem.com/selmaguedidi/a-dive-into-jenkins-configuration-as-code-jcasc-4dj0</guid>
      <description>&lt;p&gt;In today's fast-paced DevOps world, automation is key to maintaining efficiency and consistency across development, testing, and deployment processes. Jenkins, a popular open-source automation server, offers a powerful feature called Jenkins Configuration as Code (JCasC). This allows you to manage your Jenkins configurations in a human-readable and declarative manner using YAML files, ensuring your setup is consistent, reproducible, and version-controlled.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll explore the benefits of JCasC and walk through a sample use case to get you started.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Jenkins Configuration as Code (JCasC)?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Jenkins Configuration as Code (JCasC) allows you to define and configure Jenkins instances using YAML files. This means you can describe all aspects of your Jenkins setup —from global configurations to job definitions— in a version-controlled repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Dockerfile
&lt;/h2&gt;

&lt;p&gt;The Dockerfile provided below sets up Jenkins with the necessary plugins and configurations using JCasC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM jenkins/jenkins:lts-jdk17

# Install Jenkins plugins
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt

# Disable the setup wizard as we will set up Jenkins as code
ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false"

# Copy the Configuration as Code (CasC) YAML file into the image
COPY jenkins-casc.yaml /var/jenkins_home/casc_configs/jenkins.yaml

# Tell the Jenkins Configuration as Code plugin where to find the YAML file
ENV CASC_JENKINS_CONFIG="/var/jenkins_home/casc_configs/jenkins.yaml"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Dockerfile, we start with the &lt;code&gt;jenkins/jenkins:lts-jdk17&lt;/code&gt; image, ensuring we have a stable Jenkins version with &lt;code&gt;JDK 17&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;jenkins-plugin-cli&lt;/code&gt; is a command-line tool that installs Jenkins plugins specified in &lt;code&gt;plugins.txt&lt;/code&gt;, streamlining the setup of necessary plugins. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;JAVA_OPTS&lt;/code&gt; environment variable disables the Jenkins setup wizard since we are configuring Jenkins using JCasC. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COPY&lt;/code&gt; commands include the required &lt;code&gt;plugins.txt&lt;/code&gt; and &lt;code&gt;jenkins-casc.yaml&lt;/code&gt; files into the Docker image. &lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;CASC_JENKINS_CONFIG&lt;/code&gt; environment variable points Jenkins to the location of the JCasC YAML file, allowing it to automatically apply the specified configurations at startup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifying Plugins
&lt;/h2&gt;

&lt;p&gt;To ensure Jenkins has the necessary plugins for its operations, create a &lt;code&gt;plugins.txt&lt;/code&gt; file. This file lists the required plugins and their versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git:5.0.0
workflow-aggregator:600.vb_57cdd26fdd7
configuration-as-code:1810.v9b_c30a_249a_4c
matrix-auth:3.2.2
job-dsl:1.87
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line in &lt;code&gt;plugins.txt&lt;/code&gt; specifies a plugin. You can include the version after a colon (e.g., &lt;code&gt;git:5.0.0&lt;/code&gt;) to ensure a specific version is installed. If you omit the version (e.g., &lt;code&gt;git&lt;/code&gt;), the latest version of the plugin will be installed when the image is built. Specifying versions can help maintain consistency across different environments by ensuring the same plugin versions are used.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;matrix-auth&lt;/code&gt; plugin is useful for managing access control in Jenkins environments where multiple users or teams interact with Jenkins. We'll need it later when we set up users and assign them different roles based on their responsibilities.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;code&gt;job-dsl Plugin&lt;/code&gt; adds support for defining Jenkins jobs using Groovy DSL scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;configuration-as-code&lt;/code&gt; plugin is essential for JCasC to function. It enables Jenkins to read the YAML configuration file and apply the specified settings automatically. &lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Jenkins with JCasC
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;jenkins-casc.yaml&lt;/code&gt; file to configure Jenkins. In this blog, we will define user authentication, tool installations, and a sample job. Here’s a comprehensive example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jenkins:
  systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin"

  # Security Realm for user authentication
  securityRealm:
    local:
      allowsSignup: false
      users:
        - id: "admin"
          password: "admin"

        - id: "developer"
          password: "developer"

        - id: "viewer"
          password: "viewer"


  # Authorization Strategy
  authorizationStrategy:
    projectMatrix:
      entries:
        - user:
            name: admin
            permissions:
              - Overall/Administer
        - user:
            name: developer
            permissions:
              - Overall/Read
              - Job/Build
        - user:
            name: viewer
            permissions:
              - Overall/Read



# Tool Configuration
tool:
  git:
    installations:
      - name: "Default"
        home: "/usr/bin/git"
  maven:
    installations:
      - name: maven3
        properties:
          - installSource:
              installers:
                - maven:
                    id: "3.8.4"

# Sample Job Configuration
jobs:
  - script: &amp;gt;
      pipelineJob('example-pipeline-job') {
      definition {
        cps {
            script('''
                pipeline {
                    agent any
                    stages {
                        stage('Build') {
                            steps {
                                echo 'Building...'
                            }
                        }
                        stage('Test') {
                            steps {
                                echo 'Testing...'
                            }
                        }
                        stage('Deploy') {
                            steps {
                                echo 'Deploying...'
                            }
                        }
                    }
                }
            ''')
            }
          }
        } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User authentication is defined in the &lt;code&gt;securityRealm&lt;/code&gt; section, where local user authentication is configured, and self-signup is disabled for security. Three users are predefined: &lt;code&gt;admin&lt;/code&gt;, with full administrative access; &lt;code&gt;developer&lt;/code&gt;, with read and build permissions; and &lt;code&gt;viewer&lt;/code&gt;, with read-only access. This setup ensures appropriate access levels based on user roles.&lt;/p&gt;

&lt;p&gt;Authorization is handled by the Project-based Matrix Authorization Strategy in the authorizationStrategy section. This strategy provides granular control over user permissions, giving the &lt;code&gt;admin&lt;/code&gt; full control with &lt;code&gt;(Overall/Administer)&lt;/code&gt;, while both the &lt;code&gt;developer&lt;/code&gt; and &lt;code&gt;viewer&lt;/code&gt; have read access to the overall Jenkins instance &lt;code&gt;(Overall/Read)&lt;/code&gt;, with the &lt;code&gt;developer&lt;/code&gt; also having the ability to build jobs &lt;code&gt;(Job/Build)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tool section Specifies the configuration for tools that Jenkins will use, in our case: git and Maven. The git tool is set up with a default installation path, and Maven is installed ensuring that these tools are readily available for use in jobs.&lt;/p&gt;

&lt;p&gt;The jobs section defines a sample pipeline job using a script block. &lt;/p&gt;

&lt;h2&gt;
  
  
  Build and Run the Docker Container
&lt;/h2&gt;

&lt;p&gt;Now, we'll build and run our Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Build the Docker image
docker build -t jenkins-jcasc .

# Run the Docker container
docker run --name jenkins -p 8080:8080  jenkins-jcasc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the container is running, you can access Jenkins at &lt;code&gt;http://localhost:8080&lt;/code&gt; and log in using one of the user credentials created earlier.&lt;/p&gt;

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

&lt;p&gt;We can notice the system message we defined earlier and the sample pipeline.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzaor78wyqupqbghvl606.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzaor78wyqupqbghvl606.png" alt="Admin dashboard" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the code used for this blog in the following repository: &lt;a href="https://github.com/SelmaGuedidi/JCasC" rel="noopener noreferrer"&gt;https://github.com/SelmaGuedidi/JCasC&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;For further information on Jenkins Configuration as Code (JCasC), you can consult these sources:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Configuration-as-code plugin documentation:&lt;/em&gt; &lt;a href="https://plugins.jenkins.io/configuration-as-code/" rel="noopener noreferrer"&gt;https://plugins.jenkins.io/configuration-as-code/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;demos:&lt;/em&gt; &lt;a href="https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos" rel="noopener noreferrer"&gt;https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Jenkins Configuration as Code simplifies the management of Jenkins configurations, making your CI/CD pipeline more robust and easier to maintain. By leveraging Docker and JCasC, you can quickly set up consistent Jenkins environments that are easy to reproduce and update.&lt;/p&gt;

&lt;p&gt;Try out JCasC in your projects and experience the benefits of a streamlined, automated Jenkins setup. Happy automating!&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>docker</category>
      <category>devops</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
