<?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: Bhagirath</title>
    <description>The latest articles on DEV Community by Bhagirath (@bhagirath00).</description>
    <link>https://dev.to/bhagirath00</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3437194%2Fd0b3d0a9-2dea-41ad-b32a-0bb1e856c4fa.png</url>
      <title>DEV Community: Bhagirath</title>
      <link>https://dev.to/bhagirath00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bhagirath00"/>
    <language>en</language>
    <item>
      <title>End-to-End CI/CD Pipeline Using Jenkins and Kubernetes</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Tue, 20 Jan 2026 03:40:05 +0000</pubDate>
      <link>https://dev.to/bhagirath00/end-to-end-cicd-pipeline-using-jenkins-and-kubernetes-2757</link>
      <guid>https://dev.to/bhagirath00/end-to-end-cicd-pipeline-using-jenkins-and-kubernetes-2757</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Building Scalable, Cloud-Native CI/CD Pipelines with Jenkins and Kubernetes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In modern &lt;strong&gt;DevOps workflows&lt;/strong&gt;, running &lt;strong&gt;Jenkins&lt;/strong&gt; on static or long-lived build agents often leads to scalability issues, inefficient resource usage, and maintenance overhead. As applications grow and deployment frequency increases, &lt;strong&gt;CI/CD systems&lt;/strong&gt; must be dynamic, resilient, and &lt;code&gt;cloud-native&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt; solves these challenges by providing on-demand, isolated, and auto-scalable environments for Jenkins workloads. By integrating Jenkins with Kubernetes, teams can dynamically provision build agents as pods, optimize resource utilization, and build highly scalable &lt;strong&gt;CI/CD pipelines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this blog, you’ll learn how Jenkins integrates with Kubernetes for CI/CD, understand the pipeline architecture, set up Jenkins on Kubernetes, and build a production-ready &lt;strong&gt;CI/CD pipeline&lt;/strong&gt; using containerized workloads and Kubernetes deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Integrate Jenkins with Kubernetes for CI/CD?
&lt;/h2&gt;

&lt;p&gt;Kubernetes provides a robust and scalable platform for running containerized applications, and Jenkins is a powerful tool for automating the &lt;strong&gt;CI/CD pipeline&lt;/strong&gt;. When integrated, these two tools can provide significant benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Agent Provisioning&lt;/strong&gt;: &lt;strong&gt;Jenkins&lt;/strong&gt; dynamically creates &lt;strong&gt;Kubernetes pods&lt;/strong&gt; as build agents for each &lt;strong&gt;pipeline run&lt;/strong&gt;. Agents are provisioned only when needed and automatically destroyed after job completion, eliminating idle infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: &lt;strong&gt;Kubernetes&lt;/strong&gt; scales &lt;strong&gt;Jenkins&lt;/strong&gt; agents based on workload demand. Multiple pipelines can run in parallel, allowing for faster builds and testing cycles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt;: Each Jenkins job runs inside its own Kubernetes &lt;strong&gt;pod&lt;/strong&gt;, ensuring clean, reproducible, and conflict-free build environments across pipelines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud-Native Deployment&lt;/strong&gt;: Applications can be built, containerized, and deployed directly to Kubernetes *&lt;em&gt;clusters&lt;/em&gt;, enabling seamless end-to-end CI/CD workflows in cloud-native environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Efficiency&lt;/strong&gt;: Because agents are short-lived and container-based, system resources are consumed only during active pipeline execution, significantly reducing infrastructure costs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Prerequisites for Jenkins and Kubernetes CI/CD Integration
&lt;/h2&gt;

&lt;p&gt;Before integrating Jenkins with Kubernetes, ensure you have the following prerequisites in place. These prerequisites form the foundation for a stable and production-ready CI/CD setup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kubernetes Cluster&lt;/strong&gt;: A running Kubernetes cluster is required to host Jenkins agents and deploy applications. This can be a managed Kubernetes service such as &lt;strong&gt;Amazon EKS, Google GKE, Azure AKS&lt;/strong&gt;, or a self-managed on-premise &lt;strong&gt;cluster&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jenkins Installed&lt;/strong&gt;: Jenkins must be installed and accessible. It can run: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inside a Kubernetes cluster (recommended for cloud-native setups) &lt;/li&gt;
&lt;li&gt;on a standalone virtual machine or server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kubernetes Plugin for Jenkins&lt;/strong&gt;: The Kubernetes &lt;strong&gt;Plugin&lt;/strong&gt; enables Jenkins to dynamically provision Kubernetes pods as build agents. This plugin is essential for running CI/CD pipelines using Kubernetes-based agents.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cluster Access and Permissions&lt;/strong&gt;: Jenkins must &lt;strong&gt;have permission&lt;/strong&gt; to communicate with the Kubernetes API server. This is typically achieved using a Kubernetes Service Account with the required RBAC roles.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;kubectl&lt;/strong&gt;: The kubectl CLI tool is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;managing Kubernetes resources&lt;/li&gt;
&lt;li&gt;debugging deployments&lt;/li&gt;
&lt;li&gt;running deployment steps inside Jenkins pipelines&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Jenkins Kubernetes Integration Architecture
&lt;/h2&gt;

&lt;p&gt;Jenkins integrates with Kubernetes using the &lt;strong&gt;Kubernetes Plugin&lt;/strong&gt;, which allows Jenkins to run CI/CD jobs inside Kubernetes pods instead of on static build agents.&lt;/p&gt;

&lt;p&gt;In this setup, Jenkins focuses on &lt;strong&gt;orchestrating the pipeline&lt;/strong&gt;, while Kubernetes handles &lt;strong&gt;executing jobs and managing resources&lt;/strong&gt;. Whenever a pipeline starts, Jenkins asks Kubernetes to spin up a temporary pod to run the job. Once the job finishes, the pod is automatically removed.&lt;/p&gt;

&lt;p&gt;This makes the entire CI/CD system dynamic, scalable, and &lt;code&gt;cloud-native&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Jenkins and Kubernetes Work Together:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins Controller&lt;/strong&gt;: Jenkins controller manages pipelines, jobs, and credentials. It does not run builds directly. Instead, it coordinates with Kubernetes to run jobs on demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Plugin&lt;/strong&gt;: plugin connects Jenkins to the Kubernetes cluster and handles the creation and cleanup of agent pods whenever a pipeline is triggered&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes Agent Pods&lt;/strong&gt;: Each CI/CD job runs inside its own &lt;strong&gt;Kubernetes pod&lt;/strong&gt;. These pods are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created only when needed&lt;/li&gt;
&lt;li&gt;isolated from each other&lt;/li&gt;
&lt;li&gt;automatically destroyed after the job completes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jenkins Pipeline&lt;/strong&gt;: A &lt;code&gt;Jenkinsfile&lt;/code&gt; defining the CI/CD steps, including build, test, and deployment stages.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kubernetes Cluster&lt;/strong&gt;: Kubernetes cluster provides the infrastructure where agent pods run and where applications are ultimately deployed.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. CI/CD Pipeline Architecture with Jenkins and Kubernetes
&lt;/h2&gt;

&lt;p&gt;This CI/CD architecture uses Jenkins as the pipeline orchestrator and Kubernetes as the execution and deployment platform. Instead of relying on static Jenkins agents, Kubernetes dynamically provisions build agents as pods, making the pipeline scalable and resource-efficient.&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%2Fgq9pd4hmmv0evs2vggza.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%2Fgq9pd4hmmv0evs2vggza.png" alt="Integration Architecture" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1. Git
&lt;/h3&gt;

&lt;p&gt;The pipeline begins with a code change pushed to a Git repository (GitHub, GitLab, or Bitbucket).&lt;br&gt;
A webhook triggers Jenkins automatically on every commit or pull request, ensuring that no manual intervention is required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role of Git&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores application source code and &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Triggers Jenkins pipelines via webhooks&lt;/li&gt;
&lt;li&gt;Acts as the single source of truth for builds&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4.2. Jenkins Controller
&lt;/h3&gt;

&lt;p&gt;The Jenkins controller manages the CI/CD pipeline logic defined in the &lt;code&gt;Jenkinsfile&lt;/code&gt;.&lt;br&gt;
When a build is triggered, Jenkins does not execute jobs on itself. Instead, it requests Kubernetes to create an ephemeral agent pod.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Parses the &lt;code&gt;Jenkinsfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Orchestrates pipeline stages (build, test, deploy)&lt;/li&gt;
&lt;li&gt;Requests Kubernetes to provision agent pods&lt;/li&gt;
&lt;li&gt;Tracks pipeline execution and logs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4.3. Kubernetes Agent Pods (Dynamic Build Agents)
&lt;/h3&gt;

&lt;p&gt;Using the &lt;strong&gt;Jenkins Kubernetes Plugin&lt;/strong&gt;, Jenkins dynamically spins up agent pods inside &lt;strong&gt;the Kubernetes cluster&lt;/strong&gt;. Each pipeline run gets its own isolated pod, which is destroyed after completion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No long-running or idle agents&lt;/li&gt;
&lt;li&gt;Clean environment for every build&lt;/li&gt;
&lt;li&gt;Parallel pipelines without conflicts&lt;/li&gt;
&lt;li&gt;Automatic scaling based on workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2n19w2ec9g1t0eyjoxf.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%2Fl2n19w2ec9g1t0eyjoxf.png" alt="Dynamic Agents" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each agent pod can include multiple containers (for example: Maven, Docker CLI, kubectl), allowing different stages to run in the right environment.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.4. Docker Image Build &amp;amp; Push
&lt;/h3&gt;

&lt;p&gt;Inside the Kubernetes agent pod, Jenkins builds the application and creates a Docker image using the project’s &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;br&gt;
The image is then pushed to a container registry such as &lt;strong&gt;Docker Hub, Amazon ECR, or GCR&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens here&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application is compiled and tested&lt;/li&gt;
&lt;li&gt;Docker image is built inside the agent pod&lt;/li&gt;
&lt;li&gt;Image is tagged with version or commit hash&lt;/li&gt;
&lt;li&gt;Image is pushed to a container registry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the same image is used across all environments.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.5. Kubernetes Deployment
&lt;/h3&gt;

&lt;p&gt;Once the Docker image is available in the registry, Jenkins deploys the application to Kubernetes using &lt;strong&gt;kubectl&lt;/strong&gt; or Helm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment flow&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jenkins applies Kubernetes manifests or Helm charts&lt;/li&gt;
&lt;li&gt;Kubernetes pulls the image from the registry&lt;/li&gt;
&lt;li&gt;Pods are created or updated using rolling deployments&lt;/li&gt;
&lt;li&gt;Application becomes available via Service or Ingress&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This completes the &lt;strong&gt;end-to-end CI/CD&lt;/strong&gt; loop from code commit to a running application in Kubernetes.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. How to Install and Run Jenkins on Kubernetes
&lt;/h2&gt;

&lt;p&gt;Getting Jenkins up and running on Kubernetes is easier than you might think, especially with &lt;strong&gt;Helm&lt;/strong&gt;, the package manager for Kubernetes. Helm simplifies complex deployments and ensures you can get a production-ready Jenkins instance quickly.&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%2F52afz493cc27j57nc992.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%2F52afz493cc27j57nc992.png" alt="Installation of CICD" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5.1 Installing Jenkins with Helm
&lt;/h3&gt;

&lt;p&gt;The easiest way to install Jenkins on Kubernetes is using Helm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a Namespace for Jenkins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s a good practice to isolate Jenkins in its own namespace:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Install Jenkins&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Helm is a package manager for Kubernetes that simplifies the installation of complex applications like Jenkins. To install Jenkins using Helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm repo add jenkins https://charts.jenkins.io
helm repo update
helm install jenkins jenkins/jenkins --namespace jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Access Jenkins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once installed, you can access Jenkins via the Kubernetes service. To get the admin password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get svc --namespace jenkins

kubectl exec --namespace jenkins -it $(kubectl get pods --namespace jenkins -l "app.kubernetes.io/component=jenkins-master" -o jsonpath="{.items[0].metadata.name}") -- cat /run/secrets/chart-admin-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open Jenkins in your browser using the service IP and port, then log in using the retrieved admin password. &lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Configuring the Cloud
&lt;/h3&gt;

&lt;p&gt;Once Jenkins is installed, configure it to use Kubernetes for dynamic agent provisioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install the Kubernetes Plugin&lt;/strong&gt;: Go to &lt;strong&gt;Manage Jenkins &amp;gt; Manage Plugins&lt;/strong&gt; and install the &lt;strong&gt;Kubernetes Plugin&lt;/strong&gt;. This plugin allows Jenkins to communicate with your cluster and provision agents on-demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Kubernetes Cloud&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins &amp;gt; Configure System&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scroll down to &lt;strong&gt;Cloud&lt;/strong&gt; and click Add a &lt;strong&gt;new cloud &amp;gt; Kubernetes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Provide the &lt;strong&gt;Kubernetes API URL, Jenkins URL&lt;/strong&gt;, and configure the &lt;strong&gt;Kubernetes Service Account&lt;/strong&gt; so Jenkins can manage pods.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Create Pod Templates&lt;/strong&gt;: Pod templates define what containers are included in each Jenkins agent pod. You can create different templates for different types of jobs, for example:

&lt;ul&gt;
&lt;li&gt;Maven builds&lt;/li&gt;
&lt;li&gt;Docker image builds&lt;/li&gt;
&lt;li&gt;Helm deployments&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Jenkinsfile-Based CI/CD Pipeline Implementation
&lt;/h2&gt;

&lt;p&gt;With Jenkins configured to use Kubernetes, the next step is to set up CI/CD pipelines that build and deploy applications to Kubernetes.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Jenkinsfile&lt;/code&gt; allows you to describe your entire pipeline — &lt;em&gt;build, test, and deployment as code&lt;/em&gt;, making it version-controlled, repeatable, and easy to maintain.&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%2F30ssnmab8u6xwedomber.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%2F30ssnmab8u6xwedomber.png" alt="CI/CD Pipeline Implementation" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Configuring Jenkins Pipeline for Kubernetes
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Jenkinsfile&lt;/code&gt; defines what steps your pipeline runs and where they run.&lt;br&gt;
When using Kubernetes integration, Jenkins dynamically creates a pod-based agent for each pipeline execution.&lt;/p&gt;

&lt;p&gt;Here’s an example of a &lt;code&gt;Jenkinsfile&lt;/code&gt; that uses Kubernetes agents and deploys an application to a Kubernetes cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline {
    agent {
        kubernetes {
            label 'my-k8s-agent'
            defaultContainer 'jnlp'
            yaml '''
            apiVersion: v1
            kind: Pod
            spec:
              containers:
              - name: maven
                image: maven:3.9.6-eclipse-temurin-17
                command:
                - cat
                tty: true
              - name: kubectl
                image: bitnami/kubectl:latest
                command:
                - cat
                tty: true
            '''
        }
    }
    stages {
        stage('Build') {
            steps {
                container('maven') {
                    sh 'mvn clean install'
                }
            }
        }
        stage('Test') {
            steps {
                container('maven') {
                    sh 'mvn test'
                }
            }
        }
        stage('Deploy to Kubernetes') {
            steps {
                container('kubectl') {
                    sh 'kubectl apply -f deployment.yaml'
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jenkins creates a &lt;strong&gt;temporary Kubernetes pod&lt;/strong&gt; for this pipeline run&lt;/li&gt;
&lt;li&gt;The pod includes multiple containers (Maven for build/test, &lt;strong&gt;kubectl&lt;/strong&gt; for deployment)&lt;/li&gt;
&lt;li&gt;Each stage runs in the most appropriate container&lt;/li&gt;
&lt;li&gt;After the pipeline finishes, the pod is automatically destroyed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach keeps builds clean, isolated, and scalable.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 Automating Deployments to Kubernetes
&lt;/h3&gt;

&lt;p&gt;In the pipeline above, the &lt;strong&gt;Deploy to Kubernetes&lt;/strong&gt; stage uses &lt;strong&gt;kubectl&lt;/strong&gt; to apply Kubernetes manifests.&lt;br&gt;
These YAML files typically define resources such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployments&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ConfigMaps&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because deployment happens only after successful build and test stages, Jenkins ensures that &lt;strong&gt;only validated artifacts&lt;/strong&gt; reach your Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;This automation removes manual deployment steps and enables fast, consistent releases.&lt;/p&gt;
&lt;h3&gt;
  
  
  6.3 Deploying Applications with Helm
&lt;/h3&gt;

&lt;p&gt;While &lt;strong&gt;kubectl&lt;/strong&gt; apply works well, managing multiple YAML files can become difficult as applications grow.&lt;br&gt;
This is where &lt;strong&gt;Helm becomes extremely&lt;/strong&gt; useful.&lt;/p&gt;

&lt;p&gt;Helm allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package Kubernetes resources into reusable charts&lt;/li&gt;
&lt;li&gt;Version deployments&lt;/li&gt;
&lt;li&gt;Easily upgrade or roll back releases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simple &lt;code&gt;Jenkinsfile&lt;/code&gt; example that deploys an application using Helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean install'
            }
        }
        stage('Deploy to Kubernetes with Helm') {
            steps {
                sh 'helm upgrade --install myapp ./helm-chart/'
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Helm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application configuration becomes cleaner&lt;/li&gt;
&lt;li&gt;Environment-specific values are easier to manage&lt;/li&gt;
&lt;li&gt;Production deployments are more predictable&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Best Practices for Jenkins Kubernetes CI/CD Pipelines
&lt;/h2&gt;

&lt;p&gt;To get the most out of Jenkins and Kubernetes, it’s important to follow a few proven best practices. These help keep your pipelines scalable, secure, and easy to maintain as workloads grow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Pod Templates&lt;/strong&gt;: Define reusable pod templates for different job types to avoid duplication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run Each Job in an Isolated Pod&lt;/strong&gt;: Each Jenkins job should run in an isolated pod to ensure that builds are clean and independent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Auto-scaling&lt;/strong&gt;: Enable auto-scaling in Kubernetes to dynamically adjust the number of nodes based on Jenkins job demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage Secrets Securely&lt;/strong&gt;: Use Kubernetes secrets to securely manage credentials and sensitive information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Helm&lt;/strong&gt;: Package your application as a Helm chart to simplify deployment and versioning.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. Monitoring and Scaling Jenkins CI/CD Pipelines on Kubernetes
&lt;/h2&gt;

&lt;p&gt;As CI/CD pipelines grow in complexity and usage, monitoring and scaling become critical to maintaining performance and reliability. Kubernetes makes this much easier by providing built-in scalability and strong observability integrations.&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%2Fjw09ngwkehbbd0arh02i.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%2Fjw09ngwkehbbd0arh02i.png" alt="Monitoring" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring Jenkins
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jenkins Dashboard&lt;/strong&gt;: The Jenkins dashboard gives a quick, high-level view of pipeline executions, build history, and agent activity. It’s useful for tracking failed jobs, build durations, and overall pipeline health.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prometheus and Grafana&lt;/strong&gt;: For deeper visibility, Jenkins can be integrated with Prometheus and Grafana. This allows teams to monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resource usage of Jenkins controllers and agents&lt;/li&gt;
&lt;li&gt;Build and job execution metrics&lt;/li&gt;
&lt;li&gt;Pod and node performance inside the Kubernetes cluster&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Grafana dashboards make it easy to visualize trends, detect bottlenecks, and proactively address performance issues before they impact deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Jenkins with Kubernetes
&lt;/h3&gt;

&lt;p&gt;Kubernetes enables Jenkins to scale automatically based on workload demand. Jenkins agents can be created or destroyed as pods, allowing the CI/CD system to handle sudden spikes in build traffic without manual intervention.&lt;/p&gt;

&lt;p&gt;By combining Kubernetes auto-scaling with proper monitoring, teams can ensure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Builds remain fast during peak usage&lt;/li&gt;
&lt;li&gt;Infrastructure costs stay optimized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD pipelines&lt;/strong&gt; remain reliable and resilient&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Integrating &lt;strong&gt;Jenkins with Kubernetes&lt;/strong&gt; creates a modern, cloud-native CI/CD platform that is scalable, efficient, and production-ready. By running Jenkins agents as Kubernetes pods, teams can dynamically provision build environments, optimize resource usage, and eliminate the limitations of static build agents.&lt;/p&gt;

&lt;p&gt;Kubernetes features such as pod isolation, auto-scaling, and Helm-based deployments allow Jenkins pipelines to remain clean, reliable, and easy to manage as applications grow. This integration enables seamless automation—from code commits and builds to testing and deployment directly into Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;By combining &lt;strong&gt;Jenkins and Kubernetes&lt;/strong&gt;, you can build CI/CD pipelines that are faster, more resilient, and ready for real-world production workloads—making continuous delivery a natural part of your &lt;strong&gt;DevOps workflow&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>kubernetes</category>
      <category>cicdpipeline</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>How Git Stores Files Internally to Saves Space in Your Repository</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Thu, 15 Jan 2026 10:40:05 +0000</pubDate>
      <link>https://dev.to/bhagirath00/how-git-stores-files-internally-to-saves-space-in-your-repository-m4i</link>
      <guid>https://dev.to/bhagirath00/how-git-stores-files-internally-to-saves-space-in-your-repository-m4i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learn how Git stores files internally using snapshots, blobs, trees, and hashing to avoid duplication and save repository space efficiently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Git is the most widely used version control system in the world, and one of the key reasons for its popularity is its &lt;strong&gt;highly efficient storage model&lt;/strong&gt;. At first glance, Git appears to store a complete copy of your project every time you commit. Surprisingly, repositories remain compact even after thousands of commits.&lt;/p&gt;

&lt;p&gt;So how does Git duplicate files while still saving disk space?&lt;/p&gt;

&lt;p&gt;In this article, we will explore &lt;strong&gt;how Git stores files internally&lt;/strong&gt;, how it &lt;strong&gt;avoids unnecessary duplication&lt;/strong&gt;, and why its storage mechanism is both fast and &lt;strong&gt;space-efficient&lt;/strong&gt;. By the end, you will clearly understand how Git manages file data under the hood and why it scales so well for large projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  Overview: How Git Stores Data Efficiently
&lt;/h2&gt;

&lt;p&gt;Unlike traditional version control systems such as Subversion (SVN), which store &lt;strong&gt;file differences&lt;/strong&gt; between versions, Git takes a fundamentally different approach.&lt;/p&gt;

&lt;p&gt;Git stores &lt;strong&gt;snapshots of the entire project state&lt;/strong&gt; at every commit.&lt;/p&gt;

&lt;p&gt;However, Git is smart enough &lt;strong&gt;not to duplicate unchanged data&lt;/strong&gt;. If a file has not changed between commits, Git simply &lt;strong&gt;reuses the previously stored&lt;/strong&gt; version instead of saving a new copy. This design enables Git to deliver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster operations (branching, merging, checkout)&lt;/li&gt;
&lt;li&gt;Reduced disk usage&lt;/li&gt;
&lt;li&gt;Strong data integrity and reliability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. How Git Stores Data Using Snapshots Instead of File Differences
&lt;/h2&gt;

&lt;p&gt;Most version control systems track &lt;strong&gt;line-by-line changes&lt;/strong&gt; over time. Git does not.&lt;/p&gt;

&lt;p&gt;Every time you create a commit, Git records a &lt;strong&gt;snapshot of the entire file structure&lt;/strong&gt; at that moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens When Files Don’t Change?
&lt;/h3&gt;

&lt;p&gt;If a file remains unchanged between commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git does &lt;strong&gt;not&lt;/strong&gt; store the file again&lt;/li&gt;
&lt;li&gt;Git simply creates a reference to the existing stored content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means Git behaves like a &lt;strong&gt;content-addressable filesystem&lt;/strong&gt;, where identical content is stored once and referenced many times.&lt;/p&gt;

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

&lt;p&gt;This snapshot model allows Git to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instantly switch between branches&lt;/li&gt;
&lt;li&gt;Perform fast merges&lt;/li&gt;
&lt;li&gt;Avoid recalculating diffs repeatedly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Git Object Model: How Files Are Stored Internally
&lt;/h2&gt;

&lt;p&gt;Git stores all repository data as &lt;strong&gt;objects&lt;/strong&gt; inside the &lt;code&gt;.git/objects&lt;/code&gt; directory. Each object is identified by a &lt;strong&gt;cryptographic hash&lt;/strong&gt; based on its content.&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%2Fi6s3xoanno7tn1qf2po5.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%2Fi6s3xoanno7tn1qf2po5.png" alt="Git-internal-Objects" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are four primary object types in Git:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blob&lt;/strong&gt; — File contents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tree&lt;/strong&gt; — Directory structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit&lt;/strong&gt; — A snapshot with metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag&lt;/strong&gt; — Named references to commits&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.1 Blob Objects: File Content Storage
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;blob (Binary Large Object)&lt;/strong&gt; represents the &lt;strong&gt;raw content of a file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Key characteristics of blobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store file data only (no filename or permissions)&lt;/li&gt;
&lt;li&gt;Identical file contents result in &lt;strong&gt;identical blob hashes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Stored only once, regardless of how many commits reference them&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Blobs Enable De-duplication
&lt;/h3&gt;

&lt;p&gt;If two files — or the same file across commits — have identical content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git stores &lt;strong&gt;one blob&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Multiple commits point to the same blob&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the foundation of Git’s space-saving mechanism.&lt;/p&gt;

&lt;p&gt;You can inspect blobs using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git ls-tree &amp;lt;commit-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Tree Objects: Directory Structures
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;tree object&lt;/strong&gt; represents a directory in your project.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;File names&lt;/li&gt;
&lt;li&gt;File permissions&lt;/li&gt;
&lt;li&gt;References to blob objects&lt;/li&gt;
&lt;li&gt;References to other tree objects (subdirectories)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each directory in your project maps to a tree object, allowing Git to recreate the complete filesystem structure for any commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Commit Objects: Snapshots in Time
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;commit object&lt;/strong&gt; ties everything together.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A reference to the root tree&lt;/li&gt;
&lt;li&gt;Author and committer information&lt;/li&gt;
&lt;li&gt;Commit message&lt;/li&gt;
&lt;li&gt;Parent commit(s)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Commit Structure Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Commit
└── Tree (Root Directory)
    ├── Blob (File 1)
    ├── Blob (File 2)
    └── Tree (Subdirectory)
        ├── Blob (File 3)
        └── Blob (File 4)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each commit represents a &lt;strong&gt;complete snapshot&lt;/strong&gt;, but most data is reused from earlier commits.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Inside the &lt;code&gt;.git&lt;/code&gt; Directory: Git’s Internal Storage and Control System
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.git&lt;/code&gt; directory is the &lt;strong&gt;core of every Git repository&lt;/strong&gt;. It stores all metadata, objects, and references.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 &lt;code&gt;.git/objects/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This directory stores all Git objects (blobs, trees, commits) in compressed form. Objects are named using their hash values.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 &lt;code&gt;.git/refs/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;References to branches and tags live here. Each branch is simply a pointer to a commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 &lt;code&gt;.git/index&lt;/code&gt; &lt;strong&gt;(Staging Area)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The index tracks what will be included in the next commit. It bridges the gap between your working directory and the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 &lt;code&gt;.git/HEAD&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The HEAD file points to the currently checked-out branch or commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. How Git Uses Hashing, Compression, and De-duplication to Save Space
&lt;/h2&gt;

&lt;p&gt;Git’s efficiency comes from three core techniques.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Content-Addressable Hashing
&lt;/h3&gt;

&lt;p&gt;Git computes a hash (SHA-1 by default, SHA-256 supported) for every object based on its content.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same content → same hash&lt;/li&gt;
&lt;li&gt;Different content → different hash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guarantees data integrity and prevents duplication.&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%2Ff9kmwe0e49v2r4c8gcw2.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%2Ff9kmwe0e49v2r4c8gcw2.png" alt="Contnent-Addressable-Hashing" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Object Compression
&lt;/h3&gt;

&lt;p&gt;Git compresses objects using &lt;strong&gt;zlib&lt;/strong&gt;, reducing disk usage while maintaining fast access.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Automatic De-duplication
&lt;/h3&gt;

&lt;p&gt;Git never stores the same content twice. If a file hasn’t changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No new blob is created&lt;/li&gt;
&lt;li&gt;Existing blobs are reused&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how Git duplicates files logically without duplicating data physically.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. From Working Directory to Commits: How Git Builds and Stores Snapshots
&lt;/h2&gt;

&lt;p&gt;To fully understand how Git duplicates files while saving space, it is essential to understand the &lt;strong&gt;three logical areas&lt;/strong&gt; through which every change flows: the &lt;strong&gt;working directory&lt;/strong&gt;, the &lt;strong&gt;staging area&lt;/strong&gt;, and the &lt;strong&gt;commit history&lt;/strong&gt;. These are not just conceptual layers — they directly influence how Git creates objects and reuses existing data.&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%2Fpbuopnj50hjvpf3kwg1i.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%2Fpbuopnj50hjvpf3kwg1i.png" alt="Working-Directory" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Working Directory
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;working directory&lt;/strong&gt; is the actual project folder on your local machine. It contains real files that you edit using your editor or IDE.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files here exist &lt;strong&gt;outside&lt;/strong&gt; of Git’s object database&lt;/li&gt;
&lt;li&gt;Changes are not tracked automatically&lt;/li&gt;
&lt;li&gt;Git does not store anything permanently at this stage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you modify a file in the working directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git detects the change&lt;/li&gt;
&lt;li&gt;No new blob is created yet&lt;/li&gt;
&lt;li&gt;No disk space inside &lt;code&gt;.git/objects&lt;/code&gt; is used&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design allows Git to remain fast and lightweight while you experiment with changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Staging Area (Index)
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;staging area&lt;/strong&gt;, also called the &lt;strong&gt;index&lt;/strong&gt;, is where Git begins its internal storage optimization.&lt;/p&gt;

&lt;p&gt;When you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Git performs the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the file content from the working directory&lt;/li&gt;
&lt;li&gt;Computes a hash based on the content&lt;/li&gt;
&lt;li&gt;Checks whether an identical blob already exists&lt;/li&gt;
&lt;li&gt;Reuses the existing blob or creates a new one if needed&lt;/li&gt;
&lt;li&gt;Records the blob reference in &lt;code&gt;.git/index&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The staging area stores &lt;strong&gt;references&lt;/strong&gt;, not copies&lt;/li&gt;
&lt;li&gt;Unchanged files reuse existing blob objects&lt;/li&gt;
&lt;li&gt;Partial staging is supported, allowing fine-grained commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where Git’s &lt;strong&gt;de-duplication&lt;/strong&gt; logic begins to take effect.\&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 Commit History
&lt;/h3&gt;

&lt;p&gt;When you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Git creates a &lt;strong&gt;commit object&lt;/strong&gt;, which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reference to a tree object&lt;/li&gt;
&lt;li&gt;Metadata (author, timestamp, message)&lt;/li&gt;
&lt;li&gt;A reference to the parent commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crucially:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git does &lt;strong&gt;not&lt;/strong&gt; duplicate file content&lt;/li&gt;
&lt;li&gt;The new tree references existing blobs whenever possible&lt;/li&gt;
&lt;li&gt;Only changed files produce new blobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each commit represents a &lt;strong&gt;complete snapshot&lt;/strong&gt;, but internally, most data is shared across commits. This allows Git to maintain a full project history without ballooning repository size.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Exploring Git’s Internals Using Low-Level Git Commands
&lt;/h2&gt;

&lt;p&gt;One of Git’s strengths is transparency. Git provides low-level commands that allow you to &lt;strong&gt;inspect its internal object database&lt;/strong&gt;, making it easier to understand how files are stored and reused.&lt;/p&gt;

&lt;p&gt;These commands are especially valuable for developers who want to understand Git beyond everyday workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 &lt;code&gt;git cat-file&lt;/code&gt;: Viewing Raw Git Objects
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;git cat-file&lt;/code&gt; command allows you to inspect any Git object directly.&lt;/p&gt;

&lt;p&gt;To view a commit object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git cat-file -p &amp;lt;object-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;The referenced tree&lt;/li&gt;
&lt;li&gt;Parent commit&lt;/li&gt;
&lt;li&gt;Author and committer details&lt;/li&gt;
&lt;li&gt;Commit message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also inspect blob objects to see file content exactly as Git stores it, confirming that identical content is reused across commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 &lt;code&gt;git ls-tree&lt;/code&gt;: Exploring Tree Structures
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;git ls-tree&lt;/code&gt; command shows how a commit or tree maps to files and directories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git ls-tree &amp;lt;commit-hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;File permissions&lt;/li&gt;
&lt;li&gt;Object type (blob or tree)&lt;/li&gt;
&lt;li&gt;Object hash&lt;/li&gt;
&lt;li&gt;File or directory name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This command clearly demonstrates how Git builds directory snapshots using &lt;strong&gt;tree objects that reference blob objects&lt;/strong&gt;, without duplicating data.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 &lt;code&gt;git rev-parse&lt;/code&gt;: Resolving References to Hashes
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;git rev-parse&lt;/code&gt; command helps resolve symbolic references into their actual object hashes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git rev-parse HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verifying which commit a branch points to&lt;/li&gt;
&lt;li&gt;Debugging detached HEAD states&lt;/li&gt;
&lt;li&gt;Understanding reference resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reinforces the idea that &lt;strong&gt;branches and tags are lightweight pointers&lt;/strong&gt;, not copies of data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Why Git’s Storage Model Is So Powerful
&lt;/h2&gt;

&lt;p&gt;Git’s ability to duplicate files logically without duplicating data physically is the cornerstone of its performance and scalability. By storing content as immutable, hashed objects and reusing them across commits, Git ensures that repositories remain fast and space-efficient — even with extensive histories.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Git stores &lt;strong&gt;snapshots&lt;/strong&gt;, not file diffs&lt;/li&gt;
&lt;li&gt;Identical file content is stored &lt;strong&gt;only once and reused&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Blobs, trees, and commits form Git’s object model&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.git&lt;/code&gt; directory contains all internal data&lt;/li&gt;
&lt;li&gt;Hashing and compression ensure integrity and efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding Git’s internal storage model gives you deeper confidence when working with branches, rebases, merges, and large repositories. It also explains why Git continues to outperform traditional version control systems in both speed and reliability.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>productivity</category>
      <category>versioncontrol</category>
    </item>
    <item>
      <title>Why a Good README.md Matters More Than Your Code</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Mon, 01 Dec 2025 10:09:45 +0000</pubDate>
      <link>https://dev.to/bhagirath00/why-a-good-readmemd-matters-more-than-your-code-1hbg</link>
      <guid>https://dev.to/bhagirath00/why-a-good-readmemd-matters-more-than-your-code-1hbg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Is your repository a ghost town? Discover why the README.md is the most critical file in your project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The “Black Box” Problem
&lt;/h2&gt;

&lt;p&gt;Imagine you are shopping for a new laptop online. You click on a product that looks promising, but the page has no photos, no spec sheet, and no price. It just has a button that says “Buy Now.”&lt;/p&gt;

&lt;p&gt;Would you click it? Of course not. You have no idea what you are getting into.&lt;/p&gt;

&lt;p&gt;In the world of software development, your &lt;strong&gt;GitHub or GitLab repository is the product page&lt;/strong&gt;, and your &lt;strong&gt;README.md is the sales pitch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Too many developers fall into the “Black Box” trap. They spend hundreds of hours writing elegant, highly optimized algorithms, pushing perfectly tested code to the &lt;code&gt;src&lt;/code&gt; folder, and then leave the root directory empty. They assume the code speaks for itself.&lt;/p&gt;

&lt;p&gt;Code never speaks for itself. Unless a user can understand what your project does, how to install it, and why it matters in under 30 seconds, your code effectively does not exist.&lt;/p&gt;

&lt;p&gt;This guide moves beyond the theory. You’ll look at the architecture of documentation, visualize the user journey, and provide the exact syntax you need to turn a dead repository into a thriving open-source project.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Visual Impact: Before vs. After
&lt;/h2&gt;

&lt;p&gt;Let’s look at a concrete example. We have a hypothetical library called Data-Muncher, a simple Python script that cleans CSV files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario A: The “Ghost Town” (No README)
&lt;/h3&gt;

&lt;p&gt;When a recruiter or developer lands on this repository, this is all they see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📁 Data-Muncher /
├── 📁 src /
│   └── main.py
├── 📁 tests /
│   └── test_main.py
├── .gitignore
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The User Experience:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confusion&lt;/strong&gt;: “What does this do? Does it munch data? Is it for SQL or CSV?”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frustration&lt;/strong&gt;: “I have to read the source code to figure out how to run it.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: The user hits the “Back” button and finds a competitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca9r1cz902868d6q2muw.jpg" 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%2Fca9r1cz902868d6q2muw.jpg" alt="Image 1" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario B: The “Professional Product” (With README)
&lt;/h3&gt;

&lt;p&gt;Now, look at the exact same code, but with a structured &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The directory now looks like this, but the rendering on GitHub presents a beautiful interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 🦁 Data-Muncher

![Build Status](https://img.shields.io/badge/build-passing-brightgreen)
![Version](https://img.shields.io/badge/version-1.0.2-blue)
![License](https://img.shields.io/badge/license-MIT-green)

&amp;gt; A lightning-fast Python library to clean messy CSV files 10x faster than Pandas.

## 🚀 Features
- Removes duplicates automatically.
- Normalizes date formats (ISO-8601).
- zero-dependency architecture.

## 📦 Installation
pip install data-muncher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68kma8mjyuy3pp89az18.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%2F68kma8mjyuy3pp89az18.png" alt="Image 2" width="722" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The User Experience&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clarity&lt;/strong&gt;: They know exactly what it is immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust&lt;/strong&gt;: The “build passing” badge proves it works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease&lt;/strong&gt;: They can copy-paste the installation command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The “5-Second Rule”
&lt;/h2&gt;

&lt;p&gt;In UX design, we often talk about the Time to Hello World (TT-HW). This is the time it takes for a new user to land on your repo and get the code running on their machine.&lt;/p&gt;

&lt;p&gt;If your TT-HW is longer than 5 minutes, you lose 80% of your potential users.&lt;/p&gt;

&lt;h3&gt;
  
  
  The User Decision Flowchart
&lt;/h3&gt;

&lt;p&gt;Below is a diagram illustrating the mental process a developer goes through when evaluating your library.&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%2Frolgfescibaejttlpvwo.jpg" 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%2Frolgfescibaejttlpvwo.jpg" alt="Image 3" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A good README removes the “No” branches from this flowchart. It streamlines the path to the “Star the Repo” outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Technical Anatomy of a Perfect README
&lt;/h2&gt;

&lt;p&gt;A professional README isn’t just a wall of text; it is structured data using Markdown. Here are the essential components and the syntax to create them.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. The Header and Elevator Pitch
&lt;/h3&gt;

&lt;p&gt;Don’t start with “Introduction.” Start with the name and a hook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Syntax&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Project Name
**The one-line elevator pitch goes here.** *Example: "The only React Native boilerplate you will ever need."*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk36436rof52urtmptey.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%2Fqk36436rof52urtmptey.png" alt="Image 4" width="707" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Shields (Badges)
&lt;/h3&gt;

&lt;p&gt;Badges are the “Social Proof” of open source. They tell the user that the project is alive, maintained, and licensed. You don’t need complex code for this; you use markdown image links.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;![License](https://img.shields.io/badge/License-MIT-green.svg)
![Downloads](https://img.shields.io/badge/downloads-10k%2Fmonth-blue)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p20cuwxu1wgkp7a1d5j.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%2F6p20cuwxu1wgkp7a1d5j.png" alt="Image 5" width="614" height="64"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  C. The Visual Demo (Show, Don’t Tell)
&lt;/h3&gt;

&lt;p&gt;If you are building a UI, a GIF is mandatory. If you are building a CLI (Command Line Interface), a screenshot of the terminal is mandatory.&lt;/p&gt;

&lt;p&gt;Why? The human brain processes images 60,000x faster than text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;![App Demo GIF](./assets/demo.gif)
*Caption: Seeing the app in dark mode.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  D. The Quick Start (Copy-Paste Ready)
&lt;/h3&gt;

&lt;p&gt;This is the most crucial technical section. Do not describe how to install it; give the command. Use “Code Fences” (triple backticks) to allow users to copy the code easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad Documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“To install, you need to open your terminal and run the npm install command for our package.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Good Documentation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install my-awesome-package
# or
yarn add my-awesome-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzhb8qkmampr8on9vqrm.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%2Fmzhb8qkmampr8on9vqrm.png" alt="Image 6" width="725" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. README Driven Development (RDD)
&lt;/h2&gt;

&lt;p&gt;Most developers write the code first!!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Readme Driven Development (RDD)&lt;/strong&gt; suggests that you should write the README before you write a single line of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  How RDD Works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Draft the README&lt;/strong&gt;: Write down the hypothetical installation command and the API functions you wish existed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reality Check&lt;/strong&gt;: As you write the README, you might realize, “Wait, this function requires 5 arguments. That is too complicated to explain.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor Design&lt;/strong&gt;: You simplify the design before coding it, simply because explaining the complex version in the README was too hard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwclgnd57g25pecntmg51.jpg" 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%2Fwclgnd57g25pecntmg51.jpg" alt="Image 7" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Formatting Matters: Markdown Tricks for SEO and Readability
&lt;/h2&gt;

&lt;p&gt;A wall of plain text is hard to scan. You need to use Markdown features to create hierarchy and “scannability.” Search engines (SEO) also prefer structured content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Collapsible Sections
&lt;/h3&gt;

&lt;p&gt;If you have a long list of configurations, use the HTML &lt;/p&gt; tag within your Markdown to keep the page clean.

&lt;p&gt;&lt;strong&gt;Code Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Click to view Advanced Configuration&amp;lt;/summary&amp;gt;

| Option | Type | Default |
|--------|------|---------|
| --verbose | bool | false |
| --dry-run | bool | false |

&amp;lt;/details&amp;gt;
&lt;/code&gt;&lt;/pre&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%2Fy4jt7atidfoaxubu5l5h.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%2Fy4jt7atidfoaxubu5l5h.png" alt="Image 8" width="632" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Tables for Data
&lt;/h3&gt;

&lt;p&gt;Don’t list arguments in paragraphs. Use tables. They are cleaner and look professional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Syntax:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Method | Description | Returns |
|--------|-------------|---------|
| `.init()` | Starts the server | `void` |
| `.stop()` | Kills the process | `boolean` |
&lt;/code&gt;&lt;/pre&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%2Ft33hlhj36p41nrjoik98.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%2Ft33hlhj36p41nrjoik98.png" alt="Image 8" width="598" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The “Bus Factor” and Maintenance
&lt;/h2&gt;

&lt;p&gt;Documentation is an insurance policy against the “Bus Factor.”&lt;/p&gt;

&lt;p&gt;The “Bus Factor” is the minimum number of team members that have to be hit by a bus (or quit) before the project creates stops functioning because no one knows how it works.&lt;/p&gt;

&lt;p&gt;If only you understand how to deploy the database, your project has a Bus Factor of 1. This is dangerous.&lt;/p&gt;

&lt;p&gt;A good README acts as an “External Brain.” It remembers the setup steps so you don’t have to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Essential “Maintenance” Sections to Include:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development Setup&lt;/strong&gt;: How to clone and run the repo locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: How to run the test suite (&lt;code&gt;npm run test&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: How the code gets to production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. SEO: Getting Your Repo Found
&lt;/h2&gt;

&lt;p&gt;You want your project to be found on Google, not just GitHub. The &lt;code&gt;README.md&lt;/code&gt; is the primary source of content that Google crawls.&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO Checklist for READMEs:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keywords in H1/H2&lt;/strong&gt;: If your project is a “JSON Parser,” ensure those words appear in the Title and Description.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alt Text for Images&lt;/strong&gt;: Google cannot see images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bad: &lt;code&gt;![image](img.png)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Good: &lt;code&gt;![Screenshot of the JSON Parser Dashboard showing real-time metrics](img.png)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linking&lt;/strong&gt;: Link to your other projects or your portfolio. This creates a “backlink” structure that improves your ranking.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion: Documentation is Empathy
&lt;/h2&gt;

&lt;p&gt;Ultimately, writing a good README is an act of empathy. It signals that you care about the person on the other side of the screen.&lt;/p&gt;

&lt;p&gt;When a hiring manager looks at your portfolio, they aren’t going to clone your repo and audit your variable names. They are going to read your README.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messy README&lt;/strong&gt; = Messy Developer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured, Clear README&lt;/strong&gt; = Senior Engineer potential.
Don’t let your brilliant code die in the dark. Light it up with a README that sells, explains, and guides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3so84i2d5j2u5tfx99nf.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%2F3so84i2d5j2u5tfx99nf.png" alt="Image 9" width="706" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>readme</category>
    </item>
    <item>
      <title>How Kubernetes detects and restarts crashing pods automatcially</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Wed, 26 Nov 2025 17:35:33 +0000</pubDate>
      <link>https://dev.to/bhagirath00/how-kubernetes-detects-and-restarts-crashing-pods-automatcially-588e</link>
      <guid>https://dev.to/bhagirath00/how-kubernetes-detects-and-restarts-crashing-pods-automatcially-588e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A Deep Dive into the Kubelet, PLEG, and Controller Manager&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the defining promises of Kubernetes is “Self-Healing.” When a service crashes, the platform automatically detects the failure and restores the workload without human intervention. &lt;strong&gt;But How does Kubernetes restart crashing pods?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kubernetes does not technically restart &lt;strong&gt;pods&lt;/strong&gt;; it replaces the containers within them. This process is managed by the &lt;strong&gt;Kubelet&lt;/strong&gt; on each node, which uses the &lt;strong&gt;Pod Lifecycle Event Generator&lt;/strong&gt; to monitor container states. When a container fails — indicated by a non-zero exit code, an &lt;strong&gt;OOMKilled&lt;/strong&gt; signal, or a failed &lt;strong&gt;Liveness Probe&lt;/strong&gt; — the Kubelet applies a restart action. This restart is throttled by an &lt;strong&gt;exponential backoff algorithm&lt;/strong&gt; (doubling delay up to 300 seconds) to prevent CPU exhaustion, ensuring the system heals itself automatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. The Architecture of Failure: Kubelet and PLEG
&lt;/h1&gt;

&lt;p&gt;To understand detection, we must look at the &lt;strong&gt;Node Level&lt;/strong&gt;. The Kubernetes Control Plane (API Server) is often too far removed to handle immediate process failures. The heavy lifting is performed locally by the &lt;strong&gt;Kubelet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4grdzpuqwkojxfkdj64k.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%2F4grdzpuqwkojxfkdj64k.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SyncLoop
&lt;/h3&gt;

&lt;p&gt;The Kubelet runs a continuous control loop called the &lt;code&gt;SyncLoop&lt;/code&gt;. Its job is simple: &lt;strong&gt;Reconcile Expected State with Actual State&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected State&lt;/strong&gt;: “Run Nginx version 1.2.” (From API Server)&lt;br&gt;
&lt;strong&gt;Actual State&lt;/strong&gt;: “Nginx is running.” (From Runtime)&lt;/p&gt;
&lt;h3&gt;
  
  
  Problem with Polling
&lt;/h3&gt;

&lt;p&gt;In early versions of Kubernetes, the Kubelet would constantly ask the Docker daemon, “Are my containers running?” repeatedly. With 100+ pods per node, this polling choked the CPU.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: PLEG (Pod Lifecycle Event Generator)
&lt;/h3&gt;

&lt;p&gt;This is the internal mechanism that makes detection fast and efficient.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relisting&lt;/strong&gt;: PLEG periodically relists all containers from the runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparison&lt;/strong&gt;: It compares the old list with the new list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Generation&lt;/strong&gt;: If it sees a change (e.g., Container ID &lt;code&gt;abc&lt;/code&gt; changed state from &lt;code&gt;Running&lt;/code&gt; to &lt;code&gt;Exited&lt;/code&gt;), it generates a &lt;code&gt;ContainerDied&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediate Action&lt;/strong&gt;: This event wakes up the Kubelet immediately, bypassing the standard polling cycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bvu2xoarv511d2i1l99.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%2F8bvu2xoarv511d2i1l99.png" alt=" " width="799" height="425"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  2. The Three Signals of Death
&lt;/h1&gt;

&lt;p&gt;How does the runtime know a container has failed? It relies on three specific signals from the Linux Kernel and the Kubelet’s own probing logic.&lt;/p&gt;
&lt;h3&gt;
  
  
  A. Process Exit (Crash)
&lt;/h3&gt;

&lt;p&gt;When the main process inside your container (PID 1) stops, it sends an exit code to the operating system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exit Code 0&lt;/strong&gt;: The process finished successfully. (Kubernetes considers this “Completed”).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit Code 1–255&lt;/strong&gt;: The process crashed or threw an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Kubelet sees this non-zero code via the CRI and marks the container as &lt;code&gt;Error&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  B. The OOMKilled Signal (Exit Code 137)
&lt;/h3&gt;

&lt;p&gt;This is the most common and misunderstood crash.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenario&lt;/strong&gt;: Your application tries to allocate 512MB of RAM, but your Pod YAML limits it to 256MB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel’s Reaction&lt;/strong&gt;: The Linux Kernel &lt;code&gt;cgroups&lt;/code&gt; mechanism denies the memory request. The kernel invokes the &lt;strong&gt;OOM Killer&lt;/strong&gt; (Out of Memory Killer), which immediately sends &lt;code&gt;SIGKILL&lt;/code&gt; to the process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;: The container dies instantly with Exit Code 137 (128 + 9 for SIGKILL).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it looks like in &lt;code&gt;kubectl describe pod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;State:          Terminated
  Reason:       OOMKilled
  Exit Code:    137
  Started:      Mon, 01 Jan 2024 12:00:00 GMT
  Finished:     Mon, 01 Jan 2024 12:05:00 GMT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you see Exit Code 137, restarting the pod won’t fix it. You must either fix the memory leak in your code or increase the resources.limits.memory in your YAML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  C. The Liveness Probe (The Deadlock)
&lt;/h3&gt;

&lt;p&gt;Sometimes, PID 1 is still running, but the application is frozen (deadlocked) or stuck in an infinite loop. The process exists, so the kernel thinks everything is fine.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Liveness Probes&lt;/strong&gt; come in. You configure the Kubelet to actively “ping” your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;livenessProbe:
 httpGet:
 path: /healthz
 port: 8080
 initialDelaySeconds: 15
 periodSeconds: 20
 failureThreshold: 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the endpoint returns a 500 error or times out &lt;strong&gt;3 times in a row&lt;/strong&gt;, the Kubelet decides the application is broken. It forcefully kills the container to trigger a restart.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. The Recovery Logic: Restart Policies
&lt;/h1&gt;

&lt;p&gt;Once a failure is confirmed, the Kubelet consults the restartPolicy defined in the Pod spec.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;“Always” Policy (Default)&lt;/strong&gt; This is used for standard web servers and long-running services. Kubelet restarts the container regardless of why it stopped.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spec:
  restartPolicy: Always
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;“OnFailure” Policy&lt;/strong&gt; Used for batch jobs or data processing. The container is only restarted if it crashes (non-zero exit code). If it finishes cleanly (Exit Code 0), it stays stopped.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spec:
  restartPolicy: OnFailure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;“Never” Policy&lt;/strong&gt; Used for debugging or one-off static pods. Kubernetes will never restart the container.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spec:
  restartPolicy: Never
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  4. The Algorithm: CrashLoopBackOff
&lt;/h1&gt;

&lt;p&gt;Imagine your database is down, and your API crashes immediately upon connecting. If Kubernetes restarted your API instantly every time, it would restart 1,000 times a second, consuming all the CPU on the node.&lt;/p&gt;

&lt;p&gt;To prevent this, Kubernetes uses an Exponential Backoff Algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Math Behind the Wait
&lt;/h3&gt;

&lt;p&gt;When a container crashes repeatedly, the Kubelet inserts a delay before attempting the next restart. The delay doubles with every crash:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Crash 1&lt;/strong&gt;: Immediate Restart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash 2&lt;/strong&gt;: Wait 10s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash 3&lt;/strong&gt;: Wait 20s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash 4&lt;/strong&gt;: Wait 40s.&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max Delay&lt;/strong&gt;: 300s&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you run &lt;code&gt;kubectl get pods&lt;/code&gt; and see status &lt;code&gt;CrashLoopBackOff&lt;/code&gt;, it means Kubernetes is currently &lt;strong&gt;waiting&lt;/strong&gt; for this timer to expire before trying again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resetting the Timer&lt;/strong&gt;:&lt;br&gt;
The timer doesn’t last forever. If the container starts and runs successfully for &lt;strong&gt;10 minutes&lt;/strong&gt; (configurable via &lt;code&gt;minReadySeconds&lt;/code&gt;), Kubernetes resets the backoff counter to zero.&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%2Fyg4r4mrooiah38nteo69.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%2Fyg4r4mrooiah38nteo69.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  5. Cluster-Level Recovery: When the Node Dies
&lt;/h1&gt;

&lt;p&gt;The Kubelet handles local software failures. But what happens if the physical server (Node) fails? This scenario moves the responsibility from the Kubelet to the &lt;strong&gt;Kubernetes Controller Manager&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Node Controller Loop:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Heartbeat Loss&lt;/strong&gt;: Every node sends a status update to the API Server every 10 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout&lt;/strong&gt;: If the API Server receives no update for &lt;strong&gt;5 minutes&lt;/strong&gt; (default &lt;code&gt;--pod-eviction-timeout&lt;/code&gt;), the Node Controller marks the node condition as &lt;code&gt;Unknown&lt;/code&gt; or &lt;code&gt;NotReady&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eviction&lt;/strong&gt;: The controller applies a &lt;code&gt;NoExecute&lt;/code&gt; taint to the node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rescheduling&lt;/strong&gt;: The &lt;strong&gt;ReplicaSet Controller&lt;/strong&gt; observes that the number of running replicas has dropped below the desired count. It immediately schedules &lt;strong&gt;new pods&lt;/strong&gt; on remaining healthy nodes.&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%2Flzxrvcspf4w30wpkjv72.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%2Flzxrvcspf4w30wpkjv72.png" alt=" " width="799" height="425"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  6. Advanced Engineering: Startup Probes &amp;amp; Sidecars
&lt;/h1&gt;

&lt;p&gt;As Kubernetes evolves, new features allow for more granular control over restarts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Slow Start” Problem&lt;/strong&gt;&lt;br&gt;
Legacy Java apps or AI models loading large weights into GPU memory can take minutes to start. A standard Liveness Probe would kill these containers before they finish booting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;: Use a &lt;code&gt;startupProbe&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30
  periodSeconds: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logic&lt;/strong&gt;: The probe checks every 10 seconds for 30 times (300 seconds total).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior&lt;/strong&gt;: Liveness probes are disabled until the Startup probe succeeds once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  7. How to Debug Crash Loops
&lt;/h1&gt;

&lt;p&gt;When facing a crash loop, use these three commands to diagnose the root cause:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Check the Previous Logs&lt;/strong&gt; If the pod is currently crashing, standard logs might be empty. You need to see the logs of the previous instance that died.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Look for &lt;code&gt;Last State: Terminated&lt;/code&gt; and the &lt;code&gt;Exit Code&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Inspect the Events&lt;/strong&gt; The “Events” section tells you why Kubelet killed it (e.g., Liveness Probe Failed, OOMKilled).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt; --previous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for the “Last State” section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Debug with an Ephemeral Container&lt;/strong&gt; If the container crashes too fast to inspect, attach a debug shell to the running pod without restarting it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl debug -it &amp;lt;pod-name&amp;gt; --image=busybox --target=&amp;lt;container-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Kubernetes “Self-Healing” is not a single feature; it is a symphony of independent systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PLEG&lt;/strong&gt; ensures crashes are detected in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRI&lt;/strong&gt; captures the specific exit codes to determine the cause.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backoff Algorithms&lt;/strong&gt; prevent your infrastructure from being overwhelmed by failing applications.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Controllers&lt;/em&gt;* handle the catastrophic loss of physical hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding these internals, engineers can move beyond basic troubleshooting and architect systems that are resilient to both software bugs and infrastructure failures.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>kubelet</category>
      <category>pleg</category>
      <category>pods</category>
    </item>
    <item>
      <title>How Multi-Stage Builds Make Docker Images Smaller</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Fri, 19 Sep 2025 13:54:19 +0000</pubDate>
      <link>https://dev.to/bhagirath00/click-report-resolve-my-sih-2025-journey-to-smarter-civic-governance-46g0</link>
      <guid>https://dev.to/bhagirath00/click-report-resolve-my-sih-2025-journey-to-smarter-civic-governance-46g0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Docker makes it easy to package an application and its dependencies into a container image. However, the image used to build an application is not necessarily the image that should run it in production.&lt;/p&gt;

&lt;p&gt;Building an application often requires compilers, SDKs, package managers, development dependencies, and source code. Once the application has been built, many of these components are no longer required. If they remain in the final Docker image, they increase its size and add unnecessary components to the production environment.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Docker multi-stage builds&lt;/strong&gt; provide a practical solution.&lt;/p&gt;

&lt;p&gt;Multi-stage builds allow you to separate the &lt;strong&gt;build environment&lt;/strong&gt; from the &lt;strong&gt;runtime environment&lt;/strong&gt; within the same Dockerfile. The application can be built in one stage using all the required tools and dependencies, while the final stage contains only the artifacts and runtime dependencies needed to run the application.&lt;/p&gt;

&lt;p&gt;This approach can produce significantly smaller Docker images, faster deployments, and a more minimal production environment.&lt;/p&gt;

&lt;p&gt;In this article, we will examine how Docker images become unnecessarily large, how multi-stage builds solve this problem, how to implement them in a Dockerfile, and how to optimize the resulting image for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why Docker Images Get Large
&lt;/h2&gt;

&lt;p&gt;A Docker image can contain much more than the application that ultimately runs inside the container.&lt;/p&gt;

&lt;p&gt;Consider a typical application build process. Before the application can run, it may require a compiler, SDK, development libraries, package managers, source code, and other build-time dependencies. These components are necessary to create the application artifact, but they usually have no role once that artifact is ready for production.&lt;/p&gt;

&lt;p&gt;When the build and runtime environments are combined into a single Docker image, all of these components can remain in the final image.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Build Tools + Build Dependencies + Source Code + Application + Runtime Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But the production container often needs only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application + Runtime Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This difference is the main reason Docker images can become unnecessarily large.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Docker Image Size Matters
&lt;/h2&gt;

&lt;p&gt;A larger image affects more than storage. Docker images are frequently pushed to container registries and pulled by CI/CD systems, servers, and Kubernetes nodes. The larger the image, the more data needs to be transferred during these operations.&lt;/p&gt;

&lt;p&gt;Reducing Docker image size can therefore help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster image pushes and pulls&lt;/li&gt;
&lt;li&gt;Faster deployments&lt;/li&gt;
&lt;li&gt;Lower storage and registry usage&lt;/li&gt;
&lt;li&gt;Smaller production environments&lt;/li&gt;
&lt;li&gt;Reduced attack surface by excluding unnecessary build tools and packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The underlying problem is straightforward: &lt;strong&gt;the environment required to build an application is often much larger than the environment required to run it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multi-stage builds address this by keeping the build environment separate from the final runtime image.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Problem with a Single-Stage Dockerfile
&lt;/h2&gt;

&lt;p&gt;A traditional Dockerfile typically uses a single base image to both build and run the application. All build tools, dependencies, source files, and runtime components are created within the same image.&lt;/p&gt;

&lt;p&gt;For example, a compiled application may require a compiler and development dependencies during the build process. With a single-stage Dockerfile, those components remain part of the resulting image even though they are not required to run the application.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Code → Build Tools → Build Dependencies → Application → Final Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem is that the final image contains both the &lt;strong&gt;build environment&lt;/strong&gt; and the &lt;strong&gt;runtime environment&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Gets Included
&lt;/h2&gt;

&lt;p&gt;A single-stage image can contain components such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compilers and SDKs&lt;/li&gt;
&lt;li&gt;Development libraries&lt;/li&gt;
&lt;li&gt;Package managers&lt;/li&gt;
&lt;li&gt;Source code&lt;/li&gt;
&lt;li&gt;Build caches and temporary files&lt;/li&gt;
&lt;li&gt;Development dependencies&lt;/li&gt;
&lt;li&gt;The final application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these components are useful only while building the application. Once the application has been compiled or packaged, they provide no value to the production container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Approach Is Inefficient
&lt;/h2&gt;

&lt;p&gt;Using one image for both build and runtime creates an unnecessarily large production image. Every copy of that image pushed to a registry, stored in a cache, or pulled by a deployment environment carries those build-time components with it.&lt;/p&gt;

&lt;p&gt;More importantly, the production environment becomes dependent on components that should never need to exist there.&lt;/p&gt;

&lt;p&gt;The better approach is to separate the two responsibilities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Stage:&lt;/strong&gt; Contains everything required to build the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime Stage:&lt;/strong&gt; Contains only what is required to run the finished application.&lt;/p&gt;

&lt;p&gt;This separation is the fundamental idea behind Docker multi-stage builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Idea Behind Multi-Stage Builds
&lt;/h2&gt;

&lt;p&gt;Docker multi-stage builds solve the problem by separating the &lt;strong&gt;build environment&lt;/strong&gt; from the &lt;strong&gt;runtime environment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of creating the entire application inside one image, a Dockerfile can contain multiple stages. Each stage can have its own base image, dependencies, and purpose.&lt;/p&gt;

&lt;p&gt;The first stage is typically responsible for building the application. It can contain compilers, SDKs, package managers, development dependencies, and source code without worrying about their presence in the final production image.&lt;/p&gt;

&lt;p&gt;The final stage starts with a clean runtime image and copies only the artifacts required to run the application from the build stage.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Code → Build Stage → Build Artifact → Runtime Stage → Final Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, a compiled application might use a large image containing the compiler and development tools during the build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Compiler + SDK + Dependencies + Source Code → Application Binary&lt;/p&gt;

&lt;p&gt;The runtime stage then takes only the resulting binary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Minimal Base Image + Application Binary → Production Image&lt;/p&gt;

&lt;p&gt;The build environment is therefore not carried into the final image. Only the files explicitly copied from the earlier stage become part of the resulting production image.&lt;/p&gt;

&lt;p&gt;This separation is what allows multi-stage builds to produce smaller and more focused Docker images while keeping the build process inside a single Dockerfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. How Multi-Stage Builds Actually Work
&lt;/h2&gt;

&lt;p&gt;A multi-stage Dockerfile uses multiple &lt;code&gt;FROM&lt;/code&gt; instructions to create separate build stages. Each stage is independent and can contain only the tools and dependencies required for its specific purpose.&lt;/p&gt;

&lt;p&gt;A typical multi-stage build has two main stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contains the source code, compiler, SDK, and build dependencies required to produce the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contains the minimal base image and only the artifacts required to run the application.&lt;/p&gt;

&lt;p&gt;Docker allows files to be copied from one stage into another using &lt;code&gt;COPY --from&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The basic flow is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Stage → Build Artifact → Runtime Stage&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming a Build Stage
&lt;/h2&gt;

&lt;p&gt;A stage can be given a name using the &lt;code&gt;AS&lt;/code&gt; keyword:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;base-image&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name makes it possible to reference that stage later in the Dockerfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying Artifacts Between Stages
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;COPY --from&lt;/code&gt; instruction selects files from a previous stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder &amp;lt;source&amp;gt; &amp;lt;destination&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the files explicitly copied into the final stage are included there. The compiler, source code, build dependencies, and other files that remain in the build stage are not carried into the final image.&lt;/p&gt;

&lt;p&gt;This is the key mechanism behind multi-stage builds: &lt;strong&gt;build everything you need in one environment, then selectively transfer only the output required for production.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Before Multi-Stage Builds: A Single-Stage Dockerfile
&lt;/h2&gt;

&lt;p&gt;To understand the benefit of multi-stage builds, let’s first look at a typical single-stage Dockerfile.&lt;/p&gt;

&lt;p&gt;In a single-stage build, the same image is used to install build dependencies, compile the application, and run it in production. For a compiled application, this means the final image can contain both the tools used to build the application and the application itself.&lt;/p&gt;

&lt;p&gt;Consider a simple Go application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.24&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; app
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile works, but the final image is based on the Go image, which contains the Go toolchain required to compile the application.&lt;/p&gt;

&lt;p&gt;The important point is that after &lt;code&gt;go build&lt;/code&gt; finishes, the compiler and other build-time components are no longer needed to run the resulting binary.&lt;/p&gt;

&lt;p&gt;However, because everything happens in the same stage, they remain part of the final image.&lt;/p&gt;

&lt;p&gt;The resulting image therefore contains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go Toolchain + Build Dependencies + Source Code + Compiled Binary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only the compiled binary and its runtime requirements are actually needed to run the application.&lt;/p&gt;

&lt;p&gt;This is the inefficiency that multi-stage builds are designed to eliminate.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. After Multi-Stage Builds: Separating Build and Runtime
&lt;/h2&gt;

&lt;p&gt;The single-stage Dockerfile can be improved by separating the build process from the production runtime.&lt;/p&gt;

&lt;p&gt;Instead of using the Go image for the entire application lifecycle, we can use it only to build the application. A second stage can then use a smaller runtime image and copy only the compiled binary from the build stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; app

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.22&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/app .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile now has two separate stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Stage
&lt;/h2&gt;

&lt;p&gt;The first stage uses the Go image because it provides everything required to compile the application.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Go compiler and toolchain&lt;/li&gt;
&lt;li&gt;Build dependencies&lt;/li&gt;
&lt;li&gt;Source code&lt;/li&gt;
&lt;li&gt;Compiled application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this stage is used only during the build process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime Stage
&lt;/h2&gt;

&lt;p&gt;The second stage starts from a minimal Alpine image. It does not contain the Go compiler, source code, or other build-time components.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COPY --from=builder&lt;/code&gt; instruction copies only the compiled application binary from the build stage into the runtime image.&lt;/p&gt;

&lt;p&gt;The final image therefore contains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimal Runtime Image + Compiled Application&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Go Toolchain + Build Dependencies + Source Code + Compiled Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the fundamental advantage of a multi-stage Docker build: &lt;strong&gt;the build environment is used to create the application, but it does not need to be shipped with the application.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Measuring the Difference
&lt;/h2&gt;

&lt;p&gt;The main goal of using multi-stage builds is to keep unnecessary build-time components out of the final Docker image. The easiest way to verify the improvement is to compare the image sizes before and after applying a multi-stage build.&lt;/p&gt;

&lt;p&gt;For the single-stage Dockerfile, the final image is based on the Go image and therefore includes the Go toolchain and other build-time components.&lt;/p&gt;

&lt;p&gt;With the multi-stage Dockerfile, the final image is based on a minimal runtime image and contains only the compiled application.&lt;/p&gt;

&lt;p&gt;You can check the size of a Docker image using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comparison should look conceptually like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single-stage → Larger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-stage → Smaller&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The exact reduction depends on the application, its dependencies, and the selected runtime image. Multi-stage builds do not remove dependencies that the application actually needs at runtime; they remove components that are required only during the build process.&lt;/p&gt;

&lt;p&gt;The important improvement is therefore not simply that the Dockerfile has multiple stages. It is that the &lt;strong&gt;final stage contains only the components required to run the application&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Choosing the Right Runtime Image
&lt;/h2&gt;

&lt;p&gt;Once the build environment has been separated from the runtime environment, the next step is choosing an appropriate base image for the final stage.&lt;/p&gt;

&lt;p&gt;The runtime image should contain only what the application needs to run. Using a large general-purpose base image can unnecessarily increase the final Docker image size even after removing the build dependencies.&lt;/p&gt;

&lt;p&gt;Common choices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slim images:&lt;/strong&gt; Smaller versions of standard language images with fewer packages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine:&lt;/strong&gt; Lightweight Linux images commonly used when the application is compatible with Alpine’s environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distroless:&lt;/strong&gt; Images containing only the application and its runtime dependencies, without a traditional shell or package manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scratch:&lt;/strong&gt; An empty base image suitable for applications that can run without additional operating-system components, such as many statically compiled Go applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smallest image is not always the best choice. Runtime compatibility, required libraries, debugging requirements, security considerations, and operational needs should all be considered when selecting the final base image.&lt;/p&gt;

&lt;p&gt;The objective is simple: &lt;strong&gt;use the smallest practical runtime image that contains everything the application actually needs.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Multi-Stage Builds for Real Applications
&lt;/h2&gt;

&lt;p&gt;The basic Go example demonstrates the core idea, but the same pattern is useful for applications where the build process produces artifacts that are different from the source code.&lt;/p&gt;

&lt;p&gt;A common example is a frontend application built with Node.js. The Node.js environment may be required to install dependencies and compile the application, but the production container may only need the generated static files.&lt;/p&gt;

&lt;p&gt;The build process can therefore be separated into two stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Node.js + Dependencies + Source Code → Production Build&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime Stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Minimal Web Server + Production Build → Final Image&lt;/p&gt;

&lt;p&gt;For example, a frontend application can be built using a Node.js image and then served using a lightweight Nginx image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:24-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist /usr/share/nginx/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Node.js image is required only while building the application. The final image contains the generated frontend files and Nginx, but does not contain Node.js, npm, the source code, or the development dependencies.&lt;/p&gt;

&lt;p&gt;This pattern is especially useful for applications where the build environment is significantly larger than the environment required to serve the final artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Common Mistakes with Multi-Stage Builds
&lt;/h2&gt;

&lt;p&gt;Multi-stage builds can significantly reduce Docker image size, but simply adding multiple &lt;code&gt;FROM&lt;/code&gt; instructions does not automatically produce an optimized image. The final stage still needs to be designed carefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying More Than the Application Requires
&lt;/h2&gt;

&lt;p&gt;The final stage should contain only the files required at runtime. Copying the entire build directory or source tree can bring unnecessary files back into the final image.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;COPY --from&lt;/code&gt; to select only the required build artifacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Large Runtime Image
&lt;/h2&gt;

&lt;p&gt;Separating the build and runtime stages is only part of the optimization. If the final stage uses a large base image, the resulting image can still be unnecessarily large.&lt;/p&gt;

&lt;p&gt;Choose a minimal runtime image that is compatible with the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Build Dependencies in the Runtime Stage
&lt;/h2&gt;

&lt;p&gt;Installing compilers, SDKs, development libraries, or other build tools again in the final stage defeats the purpose of separating build and runtime environments.&lt;/p&gt;

&lt;p&gt;Build dependencies should remain in the build stage whenever possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Even with a multi-stage build, unnecessary files can still be sent as part of the Docker build context.&lt;/p&gt;

&lt;p&gt;A properly configured &lt;code&gt;.dockerignore&lt;/code&gt; should exclude files such as local dependencies, build output, Git metadata, logs, and other files that are not required during the image build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assuming Smaller Always Means Better
&lt;/h2&gt;

&lt;p&gt;Reducing image size is useful, but the smallest possible image is not always the correct production image. Compatibility, security, observability, debugging, and operational requirements should also be considered when choosing the runtime environment.&lt;/p&gt;

&lt;p&gt;A good multi-stage build is not simply the smallest Docker image possible. It is a &lt;strong&gt;minimal runtime image containing everything required to run the application and nothing that is only needed to build it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Other Ways to Reduce Docker Image Size
&lt;/h2&gt;

&lt;p&gt;Multi-stage builds are one of the most effective ways to separate build-time dependencies from the production image, but they are not the only way to optimize Docker image size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a Minimal Base Image
&lt;/h2&gt;

&lt;p&gt;Choose a base image that provides only what the application needs at runtime. A smaller and appropriate base image can significantly reduce the final image size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Only Required Dependencies
&lt;/h2&gt;

&lt;p&gt;Avoid installing development packages, debugging tools, or other dependencies that are not required by the application in production.&lt;/p&gt;

&lt;p&gt;For package-based applications, install production dependencies separately from development dependencies whenever possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;.dockerignore&lt;/code&gt; file prevents unnecessary files from being included in the Docker build context.&lt;/p&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Local build output&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Environment-specific files&lt;/li&gt;
&lt;li&gt;Temporary files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces the amount of unnecessary data sent to the Docker daemon or build engine during the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize Dockerfile Layering
&lt;/h2&gt;

&lt;p&gt;Arrange Dockerfile instructions so that layers that change less frequently are built before layers that change frequently. This allows Docker to reuse cached layers and avoid repeating unnecessary work during subsequent builds.&lt;/p&gt;

&lt;p&gt;These techniques complement multi-stage builds. The goal is not to rely on a single optimization, but to ensure that the final production image contains only the components required to run the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Best Practices for Multi-Stage Builds
&lt;/h2&gt;

&lt;p&gt;A well-designed multi-stage Dockerfile should keep the build process reproducible while ensuring that the final image contains only what is required at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate Build and Runtime Responsibilities
&lt;/h2&gt;

&lt;p&gt;Keep compilers, SDKs, development dependencies, and other build tools in the build stage. The final stage should focus exclusively on running the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy Only Required Artifacts
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;COPY --from&lt;/code&gt; to transfer only the files required by the runtime environment. Avoid copying source code, build caches, temporary files, or development dependencies into the final stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a Minimal Runtime Base
&lt;/h2&gt;

&lt;p&gt;Choose a runtime image appropriate for the application rather than reusing the larger build image. Consider slim, Alpine, distroless, or scratch images where they are compatible with the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize Dependency Installation
&lt;/h2&gt;

&lt;p&gt;Install only the dependencies required for the production application. Where the ecosystem supports it, keep development dependencies out of the final runtime environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take Advantage of Build Cache
&lt;/h2&gt;

&lt;p&gt;Place instructions that change less frequently earlier in the Dockerfile and frequently changing instructions later. For example, copying dependency manifests before the application source code can allow Docker to reuse the dependency installation layer when only application code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the Final Stage Simple
&lt;/h2&gt;

&lt;p&gt;The final stage should clearly show what the production container needs: the runtime base, required artifacts, configuration, and startup command.&lt;/p&gt;

&lt;p&gt;The objective of these practices is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build with everything you need, but ship only what you need.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Conclusion
&lt;/h2&gt;

&lt;p&gt;Docker multi-stage builds provide a practical way to separate the environment required to &lt;strong&gt;build&lt;/strong&gt; an application from the environment required to &lt;strong&gt;run&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;Instead of shipping compilers, SDKs, development dependencies, source code, and other build-time components in the production image, the build stage can produce the required application artifacts and the runtime stage can contain only those artifacts and their runtime dependencies.&lt;/p&gt;

&lt;p&gt;The result is a smaller and more focused Docker image that can reduce image transfer time, storage requirements, and unnecessary components in production.&lt;/p&gt;

&lt;p&gt;The core principle is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build with everything you need. Ship only what you need.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production Docker images, multi-stage builds should be considered alongside appropriate runtime base images, dependency optimization, &lt;code&gt;.dockerignore&lt;/code&gt;, and effective Docker layer caching.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
    </item>
    <item>
      <title>How 25 Lines of Code Actually Become a Production Feature</title>
      <dc:creator>Bhagirath</dc:creator>
      <pubDate>Mon, 01 Sep 2025 07:48:52 +0000</pubDate>
      <link>https://dev.to/bhagirath00/beyond-text-the-rise-of-multimodal-ai-and-its-impact-21ji</link>
      <guid>https://dev.to/bhagirath00/beyond-text-the-rise-of-multimodal-ai-and-its-impact-21ji</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;A developer opens their laptop, writes 25 lines of code, and pushes it.&lt;/p&gt;

&lt;p&gt;To them, it feels small. A bug fix. A new field. A minor feature. Twenty-five lines shouldn’t take more than a few minutes to reason about.&lt;/p&gt;

&lt;p&gt;But somewhere between that &lt;code&gt;git push&lt;/code&gt; and the moment a real user sees the change, the change has to pass through a series of engineering processes: it needs to be tracked, committed, built, tested, validated, secured, packaged, deployed, exposed to users, and eventually monitored in production.&lt;/p&gt;

&lt;p&gt;The interesting part is that no single step makes the feature production-ready. The final result comes from all of these steps working together.&lt;/p&gt;

&lt;p&gt;What looks like “just code” is actually the &lt;em&gt;smallest&lt;/em&gt; part of the journey. Dozens of tools. Multiple teams. Systems the developer never directly touches — but silently depends on.&lt;/p&gt;

&lt;p&gt;This is the story of that journey — what actually happens, step by step, between a developer’s keyboard and production.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. DevOps Lifecycle
&lt;/h2&gt;

&lt;p&gt;Before looking at individual tools, it helps to see the entire journey at once.&lt;/p&gt;

&lt;p&gt;That 25-line change doesn’t move directly from a developer’s laptop to production. It moves through a sequence of stages, with each stage answering a different question: What are we building? Can we build it? Does it work? Is it safe? Where will it run? How does it reach users? And how do we know it’s still working?&lt;/p&gt;

&lt;p&gt;The journey looks roughly like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planning → Development → Source Control → Build → Testing → Security → Infrastructure → Deployment → Networking → Monitoring → Feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each stage has a purpose — and more importantly, each stage produces something the next stage depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Planning&lt;/strong&gt; defines what should be built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development&lt;/strong&gt; turns that requirement into code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source Control&lt;/strong&gt; records what changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build&lt;/strong&gt; creates something that can be executed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt; verifies that it behaves correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; checks whether it is safe to release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure&lt;/strong&gt; provides where it will run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt; moves the application into that environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking&lt;/strong&gt; makes it reachable by users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; tells us what is happening after release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback&lt;/strong&gt; brings what we learned back into the next change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the DevOps ecosystem starts to make sense. The tools aren’t isolated pieces sitting next to each other — they exist because each stage needs to pass something forward to the next.&lt;/p&gt;

&lt;p&gt;In the rest of this journey, we’ll take this flow apart and follow those 25 lines of code through each stage, one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. From Requirement to Code
&lt;/h2&gt;

&lt;p&gt;Every feature starts before a single line of code is written.&lt;/p&gt;

&lt;p&gt;Someone decides it needs to exist — a product need, a bug report, a business requirement. That decision has to be captured, discussed, and broken down into something a developer can actually pick up and work on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jira&lt;/strong&gt; is where that happens. The requirement becomes a ticket — scoped, prioritized, and assigned. It’s the single source of truth for &lt;em&gt;what&lt;/em&gt; needs to be built and &lt;em&gt;who’s&lt;/em&gt; building it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confluence&lt;/strong&gt; sits alongside it, holding the &lt;em&gt;why&lt;/em&gt; and the &lt;em&gt;how&lt;/em&gt;. Design docs, technical decisions, API contracts, architecture notes — the context a developer needs so the ticket isn’t just a one-line instruction floating with no background.&lt;/p&gt;

&lt;p&gt;Only once the requirement is clear does the developer open their editor and start writing those 25 lines.&lt;/p&gt;

&lt;p&gt;When the code is ready, it doesn’t stay on the developer’s laptop — it moves into &lt;strong&gt;GitHub / GitLab&lt;/strong&gt;. This is the moment the change stops being personal and becomes shared. It’s tracked, versioned, reviewed by teammates, and tied back to the original Jira ticket — so anyone looking at the commit later knows exactly what it was for and why it exists.&lt;/p&gt;

&lt;p&gt;This is the first handoff in the chain:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;requirement → task → implementation → source control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code exists now — but it’s still just a change sitting in a branch. Nothing has verified that it actually works yet.&lt;/p&gt;

&lt;p&gt;That’s the next stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. From Code to a Verified Build
&lt;/h2&gt;

&lt;p&gt;The code is written. It’s sitting in a branch on GitHub or GitLab. But “written” and “working” are two very different things — and nobody ships based on trust alone.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;CI (Continuous Integration)&lt;/strong&gt; takes over. The moment that branch is pushed or a pull request is opened, tools like &lt;strong&gt;Jenkins&lt;/strong&gt;, &lt;strong&gt;GitHub Actions&lt;/strong&gt;, or &lt;strong&gt;CircleCI&lt;/strong&gt; pick it up automatically. No one has to remember to run anything manually — the pipeline triggers itself.&lt;/p&gt;

&lt;p&gt;But CI isn’t just “run the build.” It’s a sequence of checks, and the code has to clear all of them before it’s allowed to move forward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build&lt;/strong&gt; — the code is compiled and packaged into something executable. If it doesn’t even build, nothing else matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt; — tools like &lt;strong&gt;Selenium&lt;/strong&gt; run automated tests to confirm the feature actually behaves the way it’s supposed to, not just that it compiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Quality&lt;/strong&gt; — &lt;strong&gt;SonarQube&lt;/strong&gt; scans the code itself: complexity, duplication, bad patterns, maintainability issues. Code can work and still be fragile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — &lt;strong&gt;Trivy&lt;/strong&gt; scans for known vulnerabilities — in the code, in dependencies, in the container image — before any of it gets closer to production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any one of these fails, the pipeline stops right there. The 25 lines don’t move forward until every check passes.&lt;/p&gt;

&lt;p&gt;What comes out the other end isn’t just “code that runs” — it’s a &lt;strong&gt;verified build&lt;/strong&gt;: compiled, tested, quality-checked, and scanned. Something a team can actually trust enough to hand off to the next stage.&lt;/p&gt;

&lt;p&gt;And that next stage is where this verified build stops being just code — and starts becoming something that can run somewhere real.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. From Application to Infrastructure
&lt;/h2&gt;

&lt;p&gt;A verified build is still just a file sitting in a pipeline. It doesn’t run anywhere yet — it has no home. This stage is about giving it one, and it involves three distinct ideas that are easy to blur together: &lt;strong&gt;packaging&lt;/strong&gt;, &lt;strong&gt;provisioning&lt;/strong&gt;, and &lt;strong&gt;configuration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt; handles packaging. The application, along with everything it needs to run — dependencies, runtime, environment settings — gets bundled into a single image. That image behaves the same way whether it’s running on a developer’s laptop, a test server, or in production. This solves the classic “it worked on my machine” problem, because the machine itself is now part of the package.&lt;/p&gt;

&lt;p&gt;But an image still needs somewhere to run. That’s &lt;strong&gt;infrastructure&lt;/strong&gt; — the servers, networks, and storage the application will actually live on. This is where &lt;strong&gt;AWS&lt;/strong&gt;, &lt;strong&gt;GCP&lt;/strong&gt;, or &lt;strong&gt;Azure&lt;/strong&gt; come in, providing the raw compute and resources.&lt;/p&gt;

&lt;p&gt;The question is &lt;em&gt;how&lt;/em&gt; that infrastructure gets created. Doing it by hand — clicking through a cloud console — doesn’t scale and isn’t repeatable. &lt;strong&gt;Terraform&lt;/strong&gt; solves this by treating infrastructure as code: the servers, networks, and resources needed are written down in config files, and Terraform provisions exactly what’s described, every time, consistently.&lt;/p&gt;

&lt;p&gt;Once the infrastructure exists, it still needs to be set up correctly — software installed, settings applied, services configured. That’s &lt;strong&gt;Ansible&lt;/strong&gt;’s job: configuration. Terraform builds the house; Ansible furnishes it.&lt;/p&gt;

&lt;p&gt;So the distinction is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; — package the application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; — provision the infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ansible&lt;/strong&gt; — configure that infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 25 lines now exist as a packaged image, with real infrastructure ready to run it. But “ready to run” and “actually running for users” are still two different things.&lt;/p&gt;

&lt;p&gt;That’s the next stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. From Infrastructure to Production
&lt;/h2&gt;

&lt;p&gt;The infrastructure exists. The image is packaged. But right now, that image is just sitting there — nothing has actually placed it onto a running server, and no user has a way to reach it.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Kubernetes&lt;/strong&gt; takes over. It takes the Docker image and actually runs it — deciding which server it lands on, how many copies (replicas) should run for reliability, and what happens if one of those copies crashes. If a container dies, Kubernetes restarts it automatically. If traffic spikes, it can scale up more copies to handle the load. The application isn’t just deployed once — it’s &lt;em&gt;managed&lt;/em&gt;, continuously, without a human watching it 24/7.&lt;/p&gt;

&lt;p&gt;But how does that deployment actually happen — who tells Kubernetes “here’s the new version, roll it out”? That’s &lt;strong&gt;Argo CD&lt;/strong&gt;. It watches the Git repository, and the moment it sees a new verified build ready to go, it syncs that change into Kubernetes automatically. This is &lt;strong&gt;GitOps&lt;/strong&gt;: Git becomes the single source of truth for what &lt;em&gt;should&lt;/em&gt; be running, and Argo CD makes sure what’s actually running always matches it. No manual deployment steps, no one SSH-ing into a server — the desired state is declared in Git, and the system converges toward it.&lt;/p&gt;

&lt;p&gt;At this point, the application is technically live inside the cluster. But it’s still sealed off — running in an internal network Kubernetes manages, invisible to the outside world.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;Nginx&lt;/strong&gt; comes in, acting as the entry point — routing incoming requests from real users to the correct running instance of the application inside the cluster. Without it, the feature could be running perfectly and still be completely unreachable.&lt;/p&gt;

&lt;p&gt;This is the moment those 25 lines stop being “deployed” in an abstract sense and become something a real user can actually open in a browser and use.&lt;/p&gt;

&lt;p&gt;But shipping it isn’t the finish line.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Production Doesn’t Mean Finished
&lt;/h2&gt;

&lt;p&gt;The feature is live. A user can open the app and actually see those 25 lines in action. It’s tempting to treat this as the finish line — but this is exactly where things start to get interesting.&lt;/p&gt;

&lt;p&gt;Code that passed every test in a controlled pipeline can still behave differently under real traffic, real data, and real user behavior. Something might be slow under load. A dependency might fail intermittently. A memory leak might only show up after hours of runtime. None of this shows up in a test suite — it only shows up in production.&lt;/p&gt;

&lt;p&gt;That’s why &lt;strong&gt;Prometheus&lt;/strong&gt; exists. It continuously collects metrics from the running application and infrastructure — response times, error rates, CPU and memory usage, request volume — every few seconds, around the clock. It doesn’t wait for something to break; it’s always watching.&lt;/p&gt;

&lt;p&gt;Raw metrics on their own aren’t useful to a human staring at a wall of numbers. &lt;strong&gt;Grafana&lt;/strong&gt; turns that data into dashboards — graphs and visualizations that make it obvious, at a glance, whether the system is healthy or something’s drifting wrong.&lt;/p&gt;

&lt;p&gt;But dashboards only help if someone’s actually looking at them. That’s where &lt;strong&gt;Slack&lt;/strong&gt; comes in — when Prometheus detects something crossing a threshold (error rates spiking, latency climbing), an alert gets pushed straight into a team channel. The right people find out in seconds, not hours later when a user complains.&lt;/p&gt;

&lt;p&gt;This closes the loop. What’s learned in production — a bug that only shows up at scale, a slow endpoint, an unexpected failure — feeds back into Planning, becoming the next Jira ticket, the next 25 lines, the next trip through this entire pipeline.&lt;/p&gt;

&lt;p&gt;The feature didn’t just get released once. It’s now being &lt;em&gt;watched, measured, and improved&lt;/em&gt; — continuously.&lt;/p&gt;

&lt;p&gt;And that’s the real answer to the question this article started with: those 25 lines didn’t become a production feature through one step. They became one through eleven stages, dozens of tools, and a system built to make sure the loop never really closes.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
    </item>
  </channel>
</rss>
