<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ranjit sahoo</title>
    <description>The latest articles on DEV Community by ranjit sahoo (@ranjit_sahoo_480).</description>
    <link>https://dev.to/ranjit_sahoo_480</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%2F3982639%2F7fc7acf3-02fe-4607-a371-35ab7f21c0fc.png</url>
      <title>DEV Community: ranjit sahoo</title>
      <link>https://dev.to/ranjit_sahoo_480</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ranjit_sahoo_480"/>
    <language>en</language>
    <item>
      <title># Kubernetes for Absolute Beginners: Build Your First Local Cluster with kind</title>
      <dc:creator>ranjit sahoo</dc:creator>
      <pubDate>Sat, 13 Jun 2026 12:03:44 +0000</pubDate>
      <link>https://dev.to/ranjit_sahoo_480/-kubernetes-for-absolute-beginners-build-your-first-local-cluster-with-kind-445</link>
      <guid>https://dev.to/ranjit_sahoo_480/-kubernetes-for-absolute-beginners-build-your-first-local-cluster-with-kind-445</guid>
      <description>&lt;h3&gt;
  
  
  A hands-on, copy-paste guide to going from a bare Linux machine to running, scaling, and exposing real workloads on Kubernetes — no cloud account required.
&lt;/h3&gt;




&lt;p&gt;When I started learning Kubernetes, every tutorial either assumed I already had a cloud&lt;br&gt;
cluster or buried the basics under a mountain of YAML. So I did what most of us do: I&lt;br&gt;
opened a terminal, broke things, fixed them, and wrote down what actually worked.&lt;/p&gt;

&lt;p&gt;This post is that notebook, cleaned up. By the end you'll have a real Kubernetes cluster&lt;br&gt;
running on your own laptop with &lt;strong&gt;kind&lt;/strong&gt;, and you'll understand the core building blocks —&lt;br&gt;
Pods, Deployments, Services, Ingress, storage, autoscaling — not as abstract diagrams, but&lt;br&gt;
as commands you can run right now.&lt;/p&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Spin Up a Cluster on Your Own Machine
&lt;/h2&gt;

&lt;p&gt;You don't need AWS or GCP to learn Kubernetes. &lt;strong&gt;kind&lt;/strong&gt; ("Kubernetes IN Docker") runs an&lt;br&gt;
entire cluster inside Docker containers on your machine. It's fast, free, and disposable.&lt;/p&gt;

&lt;p&gt;First, install &lt;strong&gt;Docker&lt;/strong&gt; and &lt;strong&gt;kind&lt;/strong&gt; (their official docs cover every OS). Then there's&lt;br&gt;
one small gotcha on Linux: by default Docker needs &lt;code&gt;sudo&lt;/code&gt;. Fix that by adding yourself to&lt;br&gt;
the &lt;code&gt;docker&lt;/code&gt; group and refreshing your session:&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;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;newgrp docker&lt;/code&gt; reloads your group membership in the current shell, so you don't have to&lt;br&gt;
log out and back in. Now create your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kind create cluster &lt;span class="nt"&gt;--name&lt;/span&gt; practice
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;kubectl get nodes&lt;/code&gt; shows a node in &lt;code&gt;Ready&lt;/code&gt; state — congratulations, you're running&lt;br&gt;
Kubernetes.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2: Namespaces — Your Cluster's Folders
&lt;/h2&gt;

&lt;p&gt;Before deploying anything, it helps to create a &lt;strong&gt;namespace&lt;/strong&gt;: a logical partition that&lt;br&gt;
keeps your resources tidy and isolated.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You can also do it declaratively in a YAML file — and honestly, the declarative approach&lt;br&gt;
is what you should get comfortable with, because it's how real teams manage clusters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; namespace.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here on, almost every command takes a &lt;code&gt;-n &amp;lt;namespace&amp;gt;&lt;/code&gt; flag to say &lt;em&gt;where&lt;/em&gt; it should&lt;br&gt;
act.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3: Pods — The Smallest Unit That Runs
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Pod&lt;/strong&gt; is the smallest thing Kubernetes runs: one (or a few tightly-coupled) containers&lt;br&gt;
sharing a network and storage. You &lt;em&gt;can&lt;/em&gt; launch one directly:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;But here's the thing nobody tells beginners early enough: &lt;strong&gt;you almost never create bare&lt;br&gt;
Pods in real life.&lt;/strong&gt; If that pod dies, nothing brings it back. Instead, you let a higher-&lt;br&gt;
level controller — a Deployment — manage Pods for you so they self-heal and scale.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4: Deployments, and Their Cousins
&lt;/h2&gt;

&lt;p&gt;This is where Kubernetes gets powerful. A &lt;strong&gt;Deployment&lt;/strong&gt; doesn't just run your Pods — it&lt;br&gt;
keeps the desired number alive, replaces crashed ones, and rolls out new versions safely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it, then scale it 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;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yml
kubectl scale deployment/nginx-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; nginx &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you understand Deployments, three siblings fall into place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ReplicaSet&lt;/strong&gt; — keeps N identical pods running. Its YAML is almost identical to a
Deployment (just a different &lt;code&gt;kind&lt;/code&gt;), but it &lt;strong&gt;can't do rollouts or rollbacks&lt;/strong&gt;. In
practice a Deployment manages ReplicaSets for you, so you rarely write one yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DaemonSet&lt;/strong&gt; — guarantees &lt;strong&gt;one pod on every node&lt;/strong&gt; in the cluster. Perfect for
node-level agents like log shippers or monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;StatefulSet&lt;/strong&gt; — for stateful apps like databases. When a pod crashes, it comes back
with the &lt;strong&gt;same name and the same storage&lt;/strong&gt;, unlike a Deployment where replacements get
random names. This stable identity is exactly what databases need.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Updating Without Downtime (and Undoing Mistakes)
&lt;/h2&gt;

&lt;p&gt;Two concepts often get confused here, so let's be precise — because they're different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rolling update&lt;/strong&gt; is the default Deployment strategy. When you change the image, new pods&lt;br&gt;
start &lt;em&gt;before&lt;/em&gt; old ones are removed, so users never see downtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/nginx-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; nginx &lt;span class="nv"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx:1.27.3
kubectl rollout status deployment/nginx-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rollback&lt;/strong&gt; is the safety net for when an update goes wrong. Kubernetes keeps a history,&lt;br&gt;
so you can revert in seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/nginx-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
kubectl rollout undo deployment/nginx-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So: &lt;em&gt;rolling update&lt;/em&gt; = how new versions ship smoothly. &lt;em&gt;Rollback&lt;/em&gt; = how you undo a bad&lt;br&gt;
one. Don't mix them up — I did at first.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 6: Making Pods Reachable — Services &amp;amp; Ingress
&lt;/h2&gt;

&lt;p&gt;Pods are ephemeral and get new IPs constantly, so you never talk to them directly. A&lt;br&gt;
&lt;strong&gt;Service&lt;/strong&gt; gives a stable address in front of a group of pods (selected by label):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To poke at it from your laptop during development, port-forward it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward service/nginx-service &lt;span class="nt"&gt;-n&lt;/span&gt; nginx 80:80 &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For real HTTP routing — exposing multiple apps under one entry point, routing by path like&lt;br&gt;
&lt;code&gt;/apache&lt;/code&gt; and &lt;code&gt;/nginx&lt;/code&gt; — you graduate to an &lt;strong&gt;Ingress&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/$2&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/use-regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;practice.local&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/apache(/|$)(.*)&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImplementationSpecific&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-service&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things tripped me up with Ingress, so learn from my mistakes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An Ingress is &lt;strong&gt;namespaced&lt;/strong&gt; — it can only route to Services in its &lt;em&gt;own&lt;/em&gt; namespace.&lt;/li&gt;
&lt;li&gt;It does nothing until you install an &lt;strong&gt;ingress controller&lt;/strong&gt; (like ingress-nginx).&lt;/li&gt;
&lt;li&gt;Path prefixes usually need a &lt;strong&gt;rewrite annotation&lt;/strong&gt;, or your backend (which serves at
&lt;code&gt;/&lt;/code&gt;) will return 404 for &lt;code&gt;/apache&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 7: Storage That Survives a Restart
&lt;/h2&gt;

&lt;p&gt;Containers are throwaway — kill the pod and the data is gone. For anything that must&lt;br&gt;
persist (think databases), Kubernetes splits storage into two pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;PersistentVolume (PV)&lt;/strong&gt; — the actual storage in the cluster. It's &lt;strong&gt;cluster-scoped&lt;/strong&gt;,
so it has no namespace.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;PersistentVolumeClaim (PVC)&lt;/strong&gt; — a pod's &lt;em&gt;request&lt;/em&gt; for storage. It's namespaced, and
it binds to a PV that matches its size, access mode, and storage class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your pod then mounts the claim like any other volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
  &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/mysql&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
  &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql-pvc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your database keeps its data even when the pod is rescheduled.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Configuration &amp;amp; Secrets
&lt;/h2&gt;

&lt;p&gt;Hardcoding config into images is an anti-pattern. Kubernetes gives you two tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ConfigMap&lt;/strong&gt; — non-sensitive key/value config, injected as env vars or mounted files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret&lt;/strong&gt; — sensitive values (passwords, API keys), referenced via &lt;code&gt;secretKeyRef&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;
  &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql-secret&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql-root-password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important warning: a Secret is only &lt;strong&gt;base64-encoded, not encrypted&lt;/strong&gt;. Never commit&lt;br&gt;
real credentials to Git.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 9: Autoscaling — Let the Cluster React to Load
&lt;/h2&gt;

&lt;p&gt;Here's the feature that makes Kubernetes feel like magic. A &lt;strong&gt;Horizontal Pod Autoscaler&lt;br&gt;
(HPA)&lt;/strong&gt; watches a metric (usually CPU) and adds or removes pods automatically between a&lt;br&gt;
min and max you define.&lt;/p&gt;

&lt;p&gt;But HPA can't read CPU out of the box on a fresh kind cluster — you first need&lt;br&gt;
&lt;strong&gt;metrics-server&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On kind specifically, metrics-server often gets stuck because it can't verify the&lt;br&gt;
kubelet's TLS cert. The local-only fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch deployment metrics-server &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &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":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm metrics are flowing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl top nodes
kubectl top pods &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: &lt;code&gt;kubectl top nodes&lt;/code&gt; is cluster-wide, so no &lt;code&gt;-n&lt;/code&gt; flag — that one caught me.)&lt;/p&gt;

&lt;p&gt;Now define the HPA. It needs your Deployment to declare CPU &lt;strong&gt;requests&lt;/strong&gt;, then it targets&lt;br&gt;
a utilization percentage and scales between min and max replicas — and you can even tune&lt;br&gt;
&lt;em&gt;how quickly&lt;/em&gt; it reacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-hpa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-deployment&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
        &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Other Direction: Vertical Scaling with VPA
&lt;/h3&gt;

&lt;p&gt;HPA answers "how &lt;em&gt;many&lt;/em&gt; pods?" But there's a second question hiding in plain sight: "how&lt;br&gt;
&lt;em&gt;big&lt;/em&gt; should each pod be?" That's the job of the &lt;strong&gt;Vertical Pod Autoscaler (VPA)&lt;/strong&gt;. Instead&lt;br&gt;
of adding replicas, it right-sizes a pod's CPU and memory based on what it actually uses —&lt;br&gt;
so you stop guessing at &lt;code&gt;requests:&lt;/code&gt; values and stop paying for headroom you never touch.&lt;/p&gt;

&lt;p&gt;VPA isn't bundled with the cluster like HPA, so you install it from the official autoscaler&lt;br&gt;
repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/kubernetes/autoscaler.git
&lt;span class="nb"&gt;cd &lt;/span&gt;autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point it at a Deployment and choose how bold it should be with &lt;code&gt;updateMode&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VerticalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-vpa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;targetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apps/v1"&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-deployment&lt;/span&gt;
  &lt;span class="na"&gt;updatePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;updateMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Auto"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"Off"&lt;/code&gt; just hands you recommendations to eyeball (&lt;code&gt;kubectl describe vpa apache-vpa -n&lt;br&gt;
apache&lt;/code&gt;), &lt;code&gt;"Initial"&lt;/code&gt; applies them only to brand-new pods, and &lt;code&gt;"Auto"&lt;/code&gt; actively evicts and&lt;br&gt;
recreates pods to resize them on the fly.&lt;/p&gt;

&lt;p&gt;One hard-earned warning: &lt;strong&gt;never aim HPA and VPA at the same metric on the same workload.&lt;/strong&gt;&lt;br&gt;
If both manage CPU, they fight — VPA resizing pods while HPA changes their count, each one&lt;br&gt;
reacting to the other's moves. Pick one for CPU and you'll save yourself a very confusing&lt;br&gt;
afternoon.&lt;/p&gt;

&lt;p&gt;While we're on the topic of resources: every container should declare &lt;strong&gt;requests&lt;/strong&gt;&lt;br&gt;
(guaranteed) and &lt;strong&gt;limits&lt;/strong&gt; (ceiling) for CPU and memory. And if you share a cluster&lt;br&gt;
across teams, a &lt;strong&gt;ResourceQuota&lt;/strong&gt; caps total consumption per namespace.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 10: The Debugging Commands You'll Use Every Day
&lt;/h2&gt;

&lt;p&gt;When something won't start, these four commands answer 90% of "why?":&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;# Get a shell inside a running pod&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nginx-pod &lt;span class="nt"&gt;-n&lt;/span&gt; nginx &lt;span class="nt"&gt;--&lt;/span&gt; bash      &lt;span class="c"&gt;# use -- sh if bash isn't installed&lt;/span&gt;

&lt;span class="c"&gt;# See events and the real reason a pod is unhealthy&lt;/span&gt;
kubectl describe pod/nginx-pod &lt;span class="nt"&gt;-n&lt;/span&gt; nginx

&lt;span class="c"&gt;# Read the logs (-f to follow, --previous for a crashed container)&lt;/span&gt;
kubectl logs nginx-pod &lt;span class="nt"&gt;-n&lt;/span&gt; nginx &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# See everything in a namespace at once&lt;/span&gt;
kubectl get all &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small but real detail: the separator before the command is &lt;code&gt;--&lt;/code&gt; (two hyphens), and the&lt;br&gt;
shell is &lt;code&gt;bash&lt;/code&gt; or &lt;code&gt;sh&lt;/code&gt;. Copying en-dashes from notes will silently break the command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you followed along, you went from a bare machine to a running cluster where you can&lt;br&gt;
deploy apps, expose them over HTTP, give them persistent storage, secure their config, and&lt;br&gt;
let them scale themselves under load. That's genuinely most of what you do with Kubernetes&lt;br&gt;
day to day.&lt;/p&gt;

&lt;p&gt;My advice: don't just read this — &lt;code&gt;kind create cluster&lt;/code&gt; and run every command. Break a&lt;br&gt;
Deployment, watch it self-heal. Crank the HPA and hammer it with load. The concepts only&lt;br&gt;
click once you've watched the cluster react in real time.&lt;/p&gt;

&lt;p&gt;If this helped, I'd love to hear what you build next. Happy shipping! 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Follow for more hands-on DevOps and Kubernetes write-ups.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
