<?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: Md Mohiuddin</title>
    <description>The latest articles on DEV Community by Md Mohiuddin (@themdmohiuddin).</description>
    <link>https://dev.to/themdmohiuddin</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%2F3774313%2F029926fa-35c7-423a-a3b3-4a09ce9d1d00.png</url>
      <title>DEV Community: Md Mohiuddin</title>
      <link>https://dev.to/themdmohiuddin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/themdmohiuddin"/>
    <language>en</language>
    <item>
      <title>Docker Compose: Run Your Whole App with One Command</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Tue, 18 Aug 2026 14:19:02 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/docker-compose-run-your-whole-app-with-one-command-g77</link>
      <guid>https://dev.to/themdmohiuddin/docker-compose-run-your-whole-app-with-one-command-g77</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;From multiple &lt;code&gt;docker run&lt;/code&gt; commands to one declarative YAML file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A real application rarely consists of just one container.&lt;/p&gt;

&lt;p&gt;You might have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web App
   ↓
Database
   ↓
Redis
   ↓
Message Queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting and configuring all of these containers manually can quickly become painful.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Docker Compose&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;In this article, we'll learn how Docker Compose simplifies multi-container applications and how to define an entire application stack in a single YAML file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Compose Matters
&lt;/h2&gt;

&lt;p&gt;Think back to a typical multi-container setup.&lt;/p&gt;

&lt;p&gt;You might need commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create mongo-network
docker volume create mongo-data

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mongo-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo-data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password123 &lt;span class="se"&gt;\&lt;/span&gt;
  mongo

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo-express &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mongo-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ME_CONFIG_MONGODB_ADMINUSERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ME_CONFIG_MONGODB_ADMINPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="se"&gt;\&lt;/span&gt;
  mongo-express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;But it's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long&lt;/li&gt;
&lt;li&gt;Difficult to remember&lt;/li&gt;
&lt;li&gt;Easy to mistype&lt;/li&gt;
&lt;li&gt;Hard to share&lt;/li&gt;
&lt;li&gt;Difficult to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now imagine having six or ten services.&lt;/p&gt;

&lt;p&gt;You don't want to manage your application this way.&lt;/p&gt;

&lt;p&gt;Docker Compose lets you describe the entire application in a single file.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;And your application stack starts.&lt;/p&gt;

&lt;p&gt;That's the real power of Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Docker Compose?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Docker Compose is a tool for defining and running multi-container Docker applications.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of manually running individual containers, you describe your desired application architecture in a YAML file.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;

  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;

  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compose reads this configuration and creates the required containers, networks, and volumes.&lt;/p&gt;

&lt;p&gt;The important idea is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Define your application once, then let Docker Compose create and manage it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Imperative vs Declarative
&lt;/h2&gt;

&lt;p&gt;Docker Compose introduces an important DevOps concept.&lt;/p&gt;

&lt;h3&gt;
  
  
  Imperative
&lt;/h3&gt;

&lt;p&gt;With individual Docker commands, you tell Docker &lt;strong&gt;what to do&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create app-network
docker volume create database-data
docker run ...
docker run ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're specifying the steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative
&lt;/h3&gt;

&lt;p&gt;With Compose, you describe &lt;strong&gt;what you want&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;

  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I want a web service and a database service."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Compose determines how to create them.&lt;/p&gt;

&lt;p&gt;This declarative approach becomes extremely important later when you learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;Infrastructure as Code&lt;/li&gt;
&lt;li&gt;CI/CD configuration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;docker-compose.yml&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;Docker Compose uses YAML configuration.&lt;/p&gt;

&lt;p&gt;A simple file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;

  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main section is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service represents a container.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;

  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;

  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This describes three services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        Docker Compose
              |
      +-------+-------+
      |       |       |
     Web   Database  Cache
    nginx    mongo   redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  YAML Indentation Matters
&lt;/h2&gt;

&lt;p&gt;YAML uses indentation to represent structure.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;spaces&lt;/strong&gt;, not tabs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Incorrect indentation can cause YAML parsing errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Defining a Service
&lt;/h2&gt;

&lt;p&gt;Let's look at a realistic service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password123&lt;/span&gt;

    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;27017:27017"&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo-data:/data/db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of these options map directly to &lt;code&gt;docker run&lt;/code&gt; flags you've already learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;image&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Compose which Docker image to use.&lt;/p&gt;

&lt;p&gt;It's similar to:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You can also specify a version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pinning versions is generally better than relying on a moving &lt;code&gt;latest&lt;/code&gt; tag for production workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;container_name&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the container a predictable name.&lt;/p&gt;

&lt;p&gt;It's similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Compose projects, you often don't need &lt;code&gt;container_name&lt;/code&gt; because Compose already provides predictable service-based naming and DNS.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;environment&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Environment variables can be defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
  &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For real applications, avoid committing production secrets directly into your Compose file. Environment files or secret-management solutions are better choices.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;ports&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Port mappings use the same familiar syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host              Container
8080      ---&amp;gt;       80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's the Compose equivalent of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;volumes&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Volumes provide persistent storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo-data:/data/db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects the named volume &lt;code&gt;mongo-data&lt;/code&gt; to &lt;code&gt;/data/db&lt;/code&gt; inside the MongoDB container.&lt;/p&gt;

&lt;p&gt;At the bottom of the Compose file, declare the volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;build&lt;/code&gt; — Building Your Own Application
&lt;/h2&gt;

&lt;p&gt;For your own application, use &lt;code&gt;build:&lt;/code&gt; instead of &lt;code&gt;image:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Suppose your project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── Dockerfile
├── app.py
├── requirements.txt
└── docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Compose file can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;Build the image using the Dockerfile in the current directory.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;depends_on&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Suppose your web application needs MongoDB.&lt;/p&gt;

&lt;p&gt;You can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;

  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Compose to start MongoDB before starting the web container.&lt;/p&gt;

&lt;p&gt;But remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;depends_on&lt;/code&gt; controls startup order, not application readiness.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A database can still be initializing after its container has started.&lt;/p&gt;

&lt;p&gt;For more reliable startup behavior, &lt;code&gt;healthcheck&lt;/code&gt; can be used.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Compose Networking
&lt;/h2&gt;

&lt;p&gt;One of Docker Compose's biggest advantages is automatic networking.&lt;/p&gt;

&lt;p&gt;When you start a Compose project, Compose creates a network for the application by default.&lt;/p&gt;

&lt;p&gt;Services can communicate with each other using their &lt;strong&gt;service names&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;

  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The web container can connect to MongoDB using:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;as the hostname.&lt;/p&gt;

&lt;p&gt;You don't need to know MongoDB's container IP address.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------+
|      Compose Network        |
|                             |
|   +--------+   +---------+  |
|   |  web   |--&amp;gt;|  mongo  |  |
|   +--------+   +---------+  |
|                             |
+-----------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Defining a Custom Network
&lt;/h2&gt;

&lt;p&gt;You can also explicitly define your network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now both services belong to &lt;code&gt;app-network&lt;/code&gt; and can communicate with each other.&lt;/p&gt;




&lt;h2&gt;
  
  
  Restart Policies
&lt;/h2&gt;

&lt;p&gt;Compose makes it easy to define what should happen when a container stops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never automatically restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;always&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unless-stopped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart unless manually stopped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on-failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart when the process exits with an error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For many long-running services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is a useful default.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Important Compose Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start the Application
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run in the Background
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build Before Starting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Service Status
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  View Logs
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  View Logs for One Service
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Follow Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;docker compose stop&lt;/code&gt; vs &lt;code&gt;docker compose down&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This distinction is extremely important.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;docker compose stop&lt;/code&gt;
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Stops the containers but doesn't remove them.&lt;/p&gt;

&lt;p&gt;You can start them again with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;docker compose down&lt;/code&gt;
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Stops and removes the containers and the Compose-created network.&lt;/p&gt;

&lt;p&gt;By default, named volumes are preserved.&lt;/p&gt;

&lt;p&gt;That means your database data can remain intact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Be Careful With &lt;code&gt;down -v&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You can also run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; option removes the Compose-managed volumes.&lt;/p&gt;

&lt;p&gt;If your database uses those volumes, &lt;strong&gt;your database data can be deleted.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use this command carefully, especially on production systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Complete Real-World Example
&lt;/h2&gt;

&lt;p&gt;Imagine we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flask application&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;li&gt;Mongo Express&lt;/li&gt;
&lt;li&gt;Persistent MongoDB storage&lt;/li&gt;
&lt;li&gt;Custom application network&lt;/li&gt;
&lt;li&gt;Restart policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our Compose file can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flask-app&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;

    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;

    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password123&lt;/span&gt;

    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;27017:27017"&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo-data:/data/db&lt;/span&gt;

    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

  &lt;span class="na"&gt;mongo-express&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo-express&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo-express&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_MONGODB_ADMINUSERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_MONGODB_ADMINPASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password123&lt;/span&gt;

    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;

    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo&lt;/span&gt;

    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have an entire application architecture described in one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Docker Compose
                       |
          +------------+------------+
          |            |            |
       Flask App     MongoDB    Mongo Express
          |            |            |
          +------------+------------+
                       |
                 app-network
                       |
                  mongo-data
                    volume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start everything with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the services:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;Stop everything:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And bring it back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your MongoDB data remains because the named volume wasn't removed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Compose Is a Big Step Forward
&lt;/h2&gt;

&lt;p&gt;Before Compose, your workflow looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remember commands
      ↓
Create network
      ↓
Create volume
      ↓
Start database
      ↓
Start application
      ↓
Start admin UI
      ↓
Check configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up -d
        ↓
     Everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More importantly, the architecture is now stored as code.&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit it to Git&lt;/li&gt;
&lt;li&gt;Review changes&lt;/li&gt;
&lt;li&gt;Share it with teammates&lt;/li&gt;
&lt;li&gt;Reproduce the environment&lt;/li&gt;
&lt;li&gt;Use it in development&lt;/li&gt;
&lt;li&gt;Use it in CI/CD workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a major shift in how you manage applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Compose Is Infrastructure as Code Practice
&lt;/h2&gt;

&lt;p&gt;You may not think of a Compose file as infrastructure code yet.&lt;/p&gt;

&lt;p&gt;But it is teaching you the right mindset.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"I remember how I configured this server."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The configuration is defined in Git."&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"Run these 12 commands in this exact order."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here is the desired state."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This mindset becomes extremely important when you move into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;Ansible&lt;/li&gt;
&lt;li&gt;Cloud infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker Compose is one of the first practical places where you'll experience this shift.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Docker made it easy to package applications into containers.&lt;/p&gt;

&lt;p&gt;Docker networking made it possible for containers to communicate.&lt;/p&gt;

&lt;p&gt;Docker volumes made persistent data possible.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;Docker Compose brings all of these concepts together into one manageable application definition.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest lesson today isn't just learning:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It's learning to describe infrastructure &lt;strong&gt;declaratively&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of manually managing containers one by one, you define the entire application stack as code and let Docker Compose handle the implementation.&lt;/p&gt;

&lt;p&gt;That's the real bridge from simply &lt;strong&gt;using Docker&lt;/strong&gt; to thinking like a &lt;strong&gt;DevOps engineer&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Define it once. Version it. Share it. Reproduce it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>dockercompose</category>
      <category>containers</category>
    </item>
    <item>
      <title>Dockerfiles: Build Your Own Images</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Sun, 16 Aug 2026 17:29:39 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/dockerfiles-build-your-own-images-2l6m</link>
      <guid>https://dev.to/themdmohiuddin/dockerfiles-build-your-own-images-2l6m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Running Docker images is useful. Building your own images is where you start becoming a real Docker practitioner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How do you package your own application into a Docker image?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what Dockerfiles are for.&lt;/p&gt;

&lt;p&gt;A Dockerfile turns your application's code, dependencies, runtime, and startup instructions into a reproducible Docker image.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What a Dockerfile is&lt;/li&gt;
&lt;li&gt;The most important Dockerfile instructions&lt;/li&gt;
&lt;li&gt;How &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;WORKDIR&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;EXPOSE&lt;/code&gt;, and &lt;code&gt;CMD&lt;/code&gt; work&lt;/li&gt;
&lt;li&gt;Why Dockerfile instruction order matters&lt;/li&gt;
&lt;li&gt;How Docker build caching works&lt;/li&gt;
&lt;li&gt;How to build and run your own image&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;.dockerignore&lt;/code&gt; is important&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  From Docker User to Docker Author
&lt;/h2&gt;

&lt;p&gt;Until now, you've mostly worked with images created by other people.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run nginx
docker run redis
docker run mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands use images that already exist.&lt;/p&gt;

&lt;p&gt;Today, you'll create an image for an application yourself.&lt;/p&gt;

&lt;p&gt;That changes the way you think about Docker.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"How do I run this image?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll start asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How should I package this application into an image?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is an important step because Dockerfiles appear everywhere in modern software delivery.&lt;/p&gt;

&lt;p&gt;They are used by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development teams&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Cloud platforms&lt;/li&gt;
&lt;li&gt;Kubernetes deployments&lt;/li&gt;
&lt;li&gt;Container registries&lt;/li&gt;
&lt;li&gt;Production environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand Dockerfiles, you have a skill that carries directly into the rest of your DevOps journey.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Dockerfile?
&lt;/h2&gt;

&lt;p&gt;A Dockerfile is a plain-text file containing instructions that Docker uses to build an image.&lt;/p&gt;

&lt;p&gt;Think of it as a recipe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile
     |
     v
+-----------------------+
| Base image            |
| Dependencies          |
| Application code      |
| Configuration         |
| Startup command       |
+-----------------------+
     |
     v
Docker Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Dockerfile describes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which base image to use&lt;/li&gt;
&lt;li&gt;Where the application should live&lt;/li&gt;
&lt;li&gt;Which dependencies need to be installed&lt;/li&gt;
&lt;li&gt;Which files should be copied&lt;/li&gt;
&lt;li&gt;Which port the application uses&lt;/li&gt;
&lt;li&gt;Which command should run when the container starts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the Dockerfile is just a text file, you can commit it to Git alongside your application source code.&lt;/p&gt;

&lt;p&gt;That gives you a reproducible way to build the same image again and again.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;FROM&lt;/code&gt; — Choose the Base Image
&lt;/h2&gt;

&lt;p&gt;Every Dockerfile normally starts with &lt;code&gt;FROM&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:&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; python:3.12-slim&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FROM&lt;/code&gt; tells Docker which existing image should be used as the starting point.&lt;/p&gt;

&lt;p&gt;In this example, we're starting with Python 3.12 on a slim Linux base.&lt;/p&gt;

&lt;p&gt;The choice of base image matters.&lt;/p&gt;

&lt;p&gt;A smaller image generally means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster downloads&lt;/li&gt;
&lt;li&gt;Smaller storage requirements&lt;/li&gt;
&lt;li&gt;Smaller deployment artifacts&lt;/li&gt;
&lt;li&gt;Fewer unnecessary packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common Python base images include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base Image&lt;/th&gt;
&lt;th&gt;Characteristics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python:3.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full Python image with more system packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python:3.12-slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smaller image with fewer unnecessary packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python:3.12-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Very small Alpine-based image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For many applications, a slim image is a good starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Avoid &lt;code&gt;latest&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;You could write:&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; python:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the meaning of &lt;code&gt;latest&lt;/code&gt; can change over time.&lt;/p&gt;

&lt;p&gt;A future build could use a different Python version than today's build.&lt;/p&gt;

&lt;p&gt;For reproducibility, explicit versions are usually better:&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; python:3.12-slim&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The more controlled your base image is, the more predictable your builds become.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;WORKDIR&lt;/code&gt; — Set the Working Directory
&lt;/h2&gt;

&lt;p&gt;Next, define where your application will live inside the 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;WORKDIR&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 is similar to using &lt;code&gt;cd&lt;/code&gt; on Linux.&lt;/p&gt;

&lt;p&gt;After this instruction, subsequent commands operate relative to &lt;code&gt;/app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&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;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; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The files will be copied into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/app/requirements.txt
/app/app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker also creates the directory if it doesn't already exist.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;WORKDIR&lt;/code&gt; is cleaner than repeatedly writing:&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;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /app
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach is also misleading because each &lt;code&gt;RUN&lt;/code&gt; instruction executes in its own build step.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; is the standard and predictable way to define the working directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;COPY&lt;/code&gt; — Bring Application Files Into the Image
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;COPY&lt;/code&gt; instruction transfers files from your build context into the image.&lt;/p&gt;

&lt;p&gt;Example:&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; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument is the source.&lt;/p&gt;

&lt;p&gt;The second argument is the destination.&lt;/p&gt;

&lt;p&gt;Because we previously defined:&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;WORKDIR&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:&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; requirements.txt .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host:
requirements.txt

        |
        v

Container:
/app/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also copy the entire 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;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is an important reason why you should not always do this immediately.&lt;/p&gt;

&lt;p&gt;We'll come back to that when we discuss build caching.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;RUN&lt;/code&gt; — Execute Commands During the Build
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; executes a command while Docker is building the image.&lt;/p&gt;

&lt;p&gt;For a Python 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;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs the application's dependencies into the image.&lt;/p&gt;

&lt;p&gt;Other examples include:&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;RUN &lt;/span&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing to remember is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;RUN&lt;/code&gt; happens during image building.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During this process, Docker executes the &lt;code&gt;RUN&lt;/code&gt; instructions.&lt;/p&gt;

&lt;p&gt;The resulting changes become part of the image.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;RUN&lt;/code&gt; vs &lt;code&gt;CMD&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This distinction is extremely important.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; happens when the &lt;strong&gt;image is built&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; happens when the &lt;strong&gt;container is started&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think about it this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build
     |
     +--&amp;gt; RUN instructions
     |
     v
Docker Image
     |
     | docker run
     v
Container
     |
     +--&amp;gt; CMD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs dependencies during the build.&lt;/p&gt;

&lt;p&gt;Then:&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;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;starts the application when the container runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;EXPOSE&lt;/code&gt; — Document the Application Port
&lt;/h2&gt;

&lt;p&gt;Suppose your application listens on port &lt;code&gt;5000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can document that in the Dockerfile:&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;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there's an important detail:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;EXPOSE&lt;/code&gt; does not publish the port.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is primarily metadata/documentation that tells readers and tooling which port the application expects to use.&lt;/p&gt;

&lt;p&gt;To actually make the application reachable from your host, use &lt;code&gt;-p&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 my-flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mapping means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host                  Container

5000  -------------&amp;gt;  5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can reach the application inside the container.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;CMD&lt;/code&gt; — Define the Default Startup Command
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; defines what should run when a container starts.&lt;/p&gt;

&lt;p&gt;For a Python 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;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is different from &lt;code&gt;RUN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; happens during the build.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; happens when the container starts.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-flask-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;creates the image.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run my-flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;starts a container and executes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prefer Exec Form
&lt;/h3&gt;

&lt;p&gt;The recommended form is:&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;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&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;CMD&lt;/span&gt;&lt;span class="s"&gt; python app.py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON-array form is called the &lt;strong&gt;exec form&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It allows the application process to receive operating system signals more directly, which is important for graceful shutdown.&lt;/p&gt;

&lt;p&gt;For example, when you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Docker can send the termination signal to the application process.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Complete Dockerfile
&lt;/h2&gt;

&lt;p&gt;Imagine this project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-flask-app/
├── app.py
├── requirements.txt
└── Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile could look like this:&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; python:3.12-slim&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; requirements.txt .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it from top to bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM
  ↓
Choose Python

WORKDIR
  ↓
Move into /app

COPY requirements.txt
  ↓
Bring in dependencies

RUN pip install
  ↓
Install dependencies

COPY .
  ↓
Bring in application code

EXPOSE
  ↓
Document application port

CMD
  ↓
Start the application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple file describes the complete environment required to run the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Dockerfile Instruction Order Matters
&lt;/h2&gt;

&lt;p&gt;This is one of the most important Docker concepts to understand.&lt;/p&gt;

&lt;p&gt;Docker images are built in layers.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Layer 5: CMD ["python", "app.py"]
Layer 4: COPY . .
Layer 3: RUN pip install ...
Layer 2: COPY requirements.txt .
Layer 1: FROM python:3.12-slim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker can cache these layers.&lt;/p&gt;

&lt;p&gt;That means if something hasn't changed, Docker can reuse the previous result instead of doing the work again.&lt;/p&gt;

&lt;p&gt;This makes builds much faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Docker Build Cache
&lt;/h2&gt;

&lt;p&gt;Imagine your application has 20 Python dependencies.&lt;/p&gt;

&lt;p&gt;Installing them could take some time.&lt;/p&gt;

&lt;p&gt;Now imagine you change one line of &lt;code&gt;app.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You rebuild the image.&lt;/p&gt;

&lt;p&gt;Do you really want Docker to reinstall all 20 dependencies?&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;That's why we structure the Dockerfile like this:&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; requirements.txt .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency file is copied first.&lt;/p&gt;

&lt;p&gt;The application code is copied later.&lt;/p&gt;

&lt;p&gt;Now consider what happens when you change only:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Docker sees that:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;has not changed.&lt;/p&gt;

&lt;p&gt;So it can reuse the cached dependency installation layer.&lt;/p&gt;

&lt;p&gt;Only the later application layer needs to be rebuilt.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Common Dockerfile Mistake
&lt;/h2&gt;

&lt;p&gt;A beginner might write:&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; python:3.12-slim&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; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works.&lt;/p&gt;

&lt;p&gt;But it can be inefficient.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;COPY . .&lt;/code&gt; includes your application code.&lt;/p&gt;

&lt;p&gt;Every time any source file changes, Docker may invalidate that layer and everything after it.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Change app.py
      |
      v
COPY . . changes
      |
      v
pip install runs again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is unnecessary.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Better Dockerfile
&lt;/h2&gt;

&lt;p&gt;Instead, separate dependencies from application code:&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; python:3.12-slim&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; requirements.txt .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requirements.txt changes
        |
        v
Reinstall dependencies


app.py changes
        |
        v
Reuse dependency cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple ordering decision can make a huge difference during development and CI/CD builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The General Rule
&lt;/h3&gt;

&lt;p&gt;A useful principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Put instructions that change rarely before instructions that change frequently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base image
    ↓
System dependencies
    ↓
Application dependencies
    ↓
Application source code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The less frequently changing layers are placed earlier so Docker can reuse them as often as possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Your Docker Image
&lt;/h2&gt;

&lt;p&gt;Once you have a Dockerfile, build the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-flask-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;docker build&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Tells Docker to build an image.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;-t my-flask-app&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Assigns a name to the image.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-t&lt;/code&gt; option means tag.&lt;/p&gt;

&lt;p&gt;You can also specify a version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-flask-app:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful because explicit image versions make deployments easier to reproduce.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The dot specifies the &lt;strong&gt;build context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It tells Docker:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use the current directory as the build context."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Docker can access files from this context when processing instructions such as &lt;code&gt;COPY&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check Your New Image
&lt;/h2&gt;

&lt;p&gt;After the build completes:&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;You should see something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY      TAG       IMAGE ID       CREATED        SIZE
my-flask-app    latest    abc123def456   10 seconds ago  150MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use an explicit version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-flask-app:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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;might show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY      TAG       IMAGE ID
my-flask-app    1.0       abc123def456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Versioning your images becomes especially important once you start using registries and CI/CD pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Run Your Application
&lt;/h2&gt;

&lt;p&gt;Now start a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt;   &lt;span class="nt"&gt;--name&lt;/span&gt; my-app   &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000   my-flask-app:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that it's running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Check the application logs:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or open this in your browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've now taken your own application and turned it into a Docker container.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;.dockerignore&lt;/code&gt; — Your Docker Build's &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you've worked with Git, you already know about:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It prevents unwanted files from being tracked by Git.&lt;/p&gt;

&lt;p&gt;Docker has a similar concept:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It prevents unwanted files from being included in the Docker build context.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__pycache__/
*.pyc
.git/
.gitignore
.env
node_modules/
*.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is important for several reasons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller Build Context
&lt;/h3&gt;

&lt;p&gt;You don't need to send unnecessary files to Docker.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;can be very large.&lt;/p&gt;

&lt;p&gt;Ignoring them makes builds more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller Images
&lt;/h3&gt;

&lt;p&gt;If unnecessary files aren't copied into the image, the resulting image can be smaller.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Most importantly, don't accidentally copy secrets.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_PASSWORD=secret
API_KEY=abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not want those credentials baked into your Docker image.&lt;/p&gt;

&lt;p&gt;Just as you should never commit secrets to Git, you should also avoid putting secrets into container images.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A basic Python project might use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__pycache__/
*.pyc
.git/
.gitignore
.env
.venv/
venv/
*.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Node.js application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
.git/
.env
*.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact contents depend on your project.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Only send Docker the files it actually needs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;COPY&lt;/code&gt; vs &lt;code&gt;ADD&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You may also see another Dockerfile instruction:&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;ADD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ADD&lt;/code&gt; provides some additional behavior, such as handling local archives.&lt;/p&gt;

&lt;p&gt;However, for normal file copying, &lt;code&gt;COPY&lt;/code&gt; is generally preferred because its behavior is simpler and more predictable.&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;for ordinary application files.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;ADD&lt;/code&gt; only when you specifically need one of its additional features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Dockerfile Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;latest&lt;/code&gt; Everywhere
&lt;/h3&gt;

&lt;p&gt;Avoid:&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; python:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when reproducibility matters.&lt;/p&gt;

&lt;p&gt;Prefer:&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; python:3.12-slim&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Copying Everything Too Early
&lt;/h3&gt;

&lt;p&gt;Avoid:&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; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when you can separate dependencies first.&lt;/p&gt;

&lt;p&gt;Prefer:&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; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Confusing &lt;code&gt;EXPOSE&lt;/code&gt; With Port Publishing
&lt;/h3&gt;

&lt;p&gt;This:&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;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not make the application available on your host.&lt;/p&gt;

&lt;p&gt;You still need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Putting Secrets in the Image
&lt;/h3&gt;

&lt;p&gt;Never do this:&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;ENV&lt;/span&gt;&lt;span class="s"&gt; API_KEY="my-secret-key"&lt;/span&gt;
&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 docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; .env .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secrets should be provided at runtime through appropriate secret-management mechanisms rather than baked into an image.&lt;/p&gt;




&lt;h3&gt;
  
  
  Forgetting &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Without &lt;code&gt;.dockerignore&lt;/code&gt;, you may accidentally send:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git history&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Local virtual environments&lt;/li&gt;
&lt;li&gt;Environment files&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to the Docker build context.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dockerfile Mental Model
&lt;/h2&gt;

&lt;p&gt;At this point, you can think about a Dockerfile as a pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base Image
    |
    v
Working Directory
    |
    v
Application Dependencies
    |
    v
Application Code
    |
    v
Runtime Configuration
    |
    v
Startup Command
    |
    v
Docker Image
    |
    v
Docker Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each stage has a specific purpose.&lt;/p&gt;

&lt;p&gt;Once this mental model becomes familiar, Dockerfiles stop looking like mysterious configuration files.&lt;/p&gt;

&lt;p&gt;They become simple instructions describing how your application should be packaged.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Dockerfile to CI/CD
&lt;/h2&gt;

&lt;p&gt;Dockerfiles become even more powerful when combined with CI/CD.&lt;/p&gt;

&lt;p&gt;Imagine a Git push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
    |
    v
Git Push
    |
    v
CI Pipeline
    |
    v
docker build
    |
    v
Docker Image
    |
    v
Container Registry
    |
    v
Deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the most common patterns in modern software delivery.&lt;/p&gt;

&lt;p&gt;Later in your DevOps journey, you'll build CI/CD pipelines that automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build Docker images&lt;/li&gt;
&lt;li&gt;Run tests&lt;/li&gt;
&lt;li&gt;Tag images&lt;/li&gt;
&lt;li&gt;Push images to a registry&lt;/li&gt;
&lt;li&gt;Deploy them to an environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Dockerfile you write today becomes one of the core building blocks of that pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hands-On Challenge
&lt;/h2&gt;

&lt;p&gt;Create a simple Python application.&lt;/p&gt;

&lt;p&gt;Your project should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-demo/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from Docker!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;requirements.txt&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;flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Dockerfile:&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; python:3.12-slim&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; requirements.txt .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;.dockerignore&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;__pycache__/
*.pyc
.git/
.env
venv/
.venv/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-demo:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt;   &lt;span class="nt"&gt;--name&lt;/span&gt; docker-demo   &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000   docker-demo:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the container:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Check the logs:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Docker!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Docker becomes much more useful once you stop relying only on images created by others.&lt;/p&gt;

&lt;p&gt;A Dockerfile gives you a reproducible way to package your own application with its runtime and dependencies.&lt;/p&gt;

&lt;p&gt;The most important concepts from today are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FROM&lt;/code&gt; defines the base image&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WORKDIR&lt;/code&gt; defines the working directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COPY&lt;/code&gt; brings files into the image&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUN&lt;/code&gt; executes commands during the build&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPOSE&lt;/code&gt; documents the application's port&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CMD&lt;/code&gt; defines the default startup command&lt;/li&gt;
&lt;li&gt;Docker layers make build caching possible&lt;/li&gt;
&lt;li&gt;Instruction order can dramatically improve build performance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.dockerignore&lt;/code&gt; keeps unnecessary and sensitive files out of the build context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important habit to take away is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build images to be reproducible, small, secure, and easy to rebuild.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you can confidently write a Dockerfile for your own application, you're no longer just running containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're building the containers that power the rest of your DevOps workflow.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Docker Networking &amp; Volumes: Connecting Containers and Persisting Data</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Fri, 14 Aug 2026 12:37:43 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/docker-networking-volumes-connecting-containers-and-persisting-data-56fn</link>
      <guid>https://dev.to/themdmohiuddin/docker-networking-volumes-connecting-containers-and-persisting-data-56fn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learn how containers communicate with each other and how to keep data alive even after containers are removed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modern applications rarely run as a single container. A typical application might include a web application, a database, a cache layer, and background workers. For these services to work together, containers need a reliable way to communicate and share data.&lt;/p&gt;

&lt;p&gt;In this article, we'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How Docker networking works&lt;/li&gt;
&lt;li&gt;How containers discover each other&lt;/li&gt;
&lt;li&gt;Docker network drivers&lt;/li&gt;
&lt;li&gt;Persistent storage with Docker volumes&lt;/li&gt;
&lt;li&gt;Essential networking and volume commands&lt;/li&gt;
&lt;li&gt;A real-world multi-container example&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, we'll understand two of the most important concepts in Docker: &lt;strong&gt;networking&lt;/strong&gt; and &lt;strong&gt;data persistence&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Networking Matters
&lt;/h2&gt;

&lt;p&gt;Every container runs inside its own isolated network namespace.&lt;/p&gt;

&lt;p&gt;This isolation improves security and prevents conflicts, but it also creates an important challenge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If containers are isolated, how does a web application connect to a database?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine a web application running inside one container and MongoDB running inside another. Without networking, they cannot communicate.&lt;/p&gt;

&lt;p&gt;Docker solves this problem using &lt;strong&gt;Docker Networks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Docker network allows containers to communicate with each other while remaining isolated from unrelated containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web App Container
        |
        v
   Docker Network
        |
        v
Database Container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without a shared network, containers cannot easily find or communicate with each other.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Network Drivers
&lt;/h2&gt;

&lt;p&gt;Docker supports several network drivers, but most developers primarily use three.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bridge Network
&lt;/h3&gt;

&lt;p&gt;A bridge network creates a private virtual network on the Docker host.&lt;/p&gt;

&lt;p&gt;Containers connected to the same bridge network can communicate with each other securely.&lt;/p&gt;

&lt;p&gt;Create a custom bridge network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits of bridge networks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container-to-container communication&lt;/li&gt;
&lt;li&gt;Isolation from other applications&lt;/li&gt;
&lt;li&gt;Built-in DNS resolution&lt;/li&gt;
&lt;li&gt;Easy management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most Docker projects, a user-defined bridge network is the recommended choice.&lt;/p&gt;




&lt;h3&gt;
  
  
  Host Network
&lt;/h3&gt;

&lt;p&gt;With the host driver, the container shares the host machine's network stack directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--network&lt;/span&gt; host nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slightly better networking performance&lt;/li&gt;
&lt;li&gt;No port mapping required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduced isolation&lt;/li&gt;
&lt;li&gt;Potential port conflicts&lt;/li&gt;
&lt;li&gt;Less flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most applications, bridge networks are the better option.&lt;/p&gt;




&lt;h3&gt;
  
  
  None Network
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;none&lt;/code&gt; driver completely disables networking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--network&lt;/span&gt; none alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A container using this driver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cannot access the internet&lt;/li&gt;
&lt;li&gt;Cannot communicate with other containers&lt;/li&gt;
&lt;li&gt;Cannot accept incoming connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for highly restricted workloads that require no network access.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Important Feature: Service Discovery
&lt;/h2&gt;

&lt;p&gt;One of Docker's most powerful networking features is built-in DNS resolution.&lt;/p&gt;

&lt;p&gt;Instead of connecting containers by IP address, we can connect them using container names.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with IP Addresses
&lt;/h3&gt;

&lt;p&gt;Container IP addresses are assigned dynamically.&lt;/p&gt;

&lt;p&gt;If a container restarts, its IP address can change.&lt;/p&gt;

&lt;p&gt;Hardcoding IP addresses creates fragile configurations that eventually break.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.25 → MongoDB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If MongoDB restarts and receives a new IP address, the application can no longer connect.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Better Approach
&lt;/h3&gt;

&lt;p&gt;Create a custom network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; my-app-network &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Mongo Express:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo-express &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; my-app-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="se"&gt;\&lt;/span&gt;
  mongo-express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice this environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mongo Express connects to MongoDB using the container name.&lt;/p&gt;

&lt;p&gt;Docker automatically resolves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongo → container IP address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature is called &lt;strong&gt;service discovery&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of relying on changing IP addresses, containers communicate using stable names.&lt;/p&gt;




&lt;h2&gt;
  
  
  Essential Docker Network Commands
&lt;/h2&gt;

&lt;p&gt;List all networks:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create a network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect a network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network inspect my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect a running container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network connect my-app-network my-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Disconnect a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network disconnect my-app-network my-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove a network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network &lt;span class="nb"&gt;rm &lt;/span&gt;my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands are the foundation of Docker networking and are frequently used when troubleshooting multi-container applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Volumes Matter
&lt;/h2&gt;

&lt;p&gt;Containers are designed to be disposable.&lt;/p&gt;

&lt;p&gt;If a container is removed, any data stored inside its writable layer is lost forever.&lt;/p&gt;

&lt;p&gt;For applications such as databases, this is a major problem.&lt;/p&gt;

&lt;p&gt;Imagine storing customer information in MongoDB and then deleting the container.&lt;/p&gt;

&lt;p&gt;Without persistent storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Container removed = Data lost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where Docker Volumes become essential.&lt;/p&gt;

&lt;p&gt;Docker Volumes allow data to exist independently from containers.&lt;/p&gt;

&lt;p&gt;Even if a container is removed and recreated, the data remains intact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Types of Docker Storage
&lt;/h2&gt;

&lt;p&gt;Docker provides multiple ways to persist data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Named Volumes
&lt;/h3&gt;

&lt;p&gt;Named volumes are the recommended approach for most production workloads.&lt;/p&gt;

&lt;p&gt;Create a volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create mongo-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo-data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed by Docker&lt;/li&gt;
&lt;li&gt;Portable across environments&lt;/li&gt;
&lt;li&gt;Easy backups and maintenance&lt;/li&gt;
&lt;li&gt;Cleaner configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For databases and production applications, named volumes are usually the best choice.&lt;/p&gt;




&lt;h3&gt;
  
  
  Bind Mounts
&lt;/h3&gt;

&lt;p&gt;Bind mounts connect a specific host directory to a container.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /home/user/data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct access to files from the host&lt;/li&gt;
&lt;li&gt;Great for development workflows&lt;/li&gt;
&lt;li&gt;Easy editing of source code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depends on host filesystem paths&lt;/li&gt;
&lt;li&gt;Less portable&lt;/li&gt;
&lt;li&gt;Can introduce permission issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bind mounts are commonly used during development, while named volumes are preferred for production workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Volume Mapping
&lt;/h2&gt;

&lt;p&gt;Volume syntax follows this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;:destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-v&lt;/span&gt; mongo-data:/data/db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongo-data   → Host side
/data/db     → Container side
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the container writes data to &lt;code&gt;/data/db&lt;/code&gt;, that data is stored in the Docker volume named &lt;code&gt;mongo-data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is similar to Docker port mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8080 → Host port
80   → Container port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same "host-to-container" concept applies to both networking and storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Essential Docker Volume Commands
&lt;/h2&gt;

&lt;p&gt;List all volumes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create a volume:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Inspect a volume:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Remove a volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume &lt;span class="nb"&gt;rm &lt;/span&gt;my-volume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove unused volumes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;⚠️ Use &lt;code&gt;docker volume prune&lt;/code&gt; carefully.&lt;/p&gt;

&lt;p&gt;It permanently deletes unused volumes and can remove important data if executed without checking first.&lt;/p&gt;

&lt;p&gt;A good habit is to review existing volumes before deleting anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Real Multi-Container Application
&lt;/h2&gt;

&lt;p&gt;Let's combine networking and volumes into a realistic example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Network
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create my-app-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a Volume
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create mongo-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Start MongoDB
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; my-app-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo-data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Start Mongo Express
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongo-express &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; my-app-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="se"&gt;\&lt;/span&gt;
  mongo-express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB stores its data in a persistent volume.&lt;/li&gt;
&lt;li&gt;Mongo Express shares the same Docker network.&lt;/li&gt;
&lt;li&gt;Mongo Express discovers MongoDB using the hostname &lt;code&gt;mongo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Data survives container recreation.&lt;/li&gt;
&lt;li&gt;Both services remain isolated from unrelated containers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a real-world pattern used in countless Docker applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Networking and Volumes Work Together
&lt;/h2&gt;

&lt;p&gt;A successful containerized application usually needs both networking and persistence.&lt;/p&gt;

&lt;p&gt;Networking provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communication between services&lt;/li&gt;
&lt;li&gt;Service discovery&lt;/li&gt;
&lt;li&gt;Isolation between applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Volumes provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persistent storage&lt;/li&gt;
&lt;li&gt;Data durability&lt;/li&gt;
&lt;li&gt;Independence from container lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without networking, services cannot communicate.&lt;/p&gt;

&lt;p&gt;Without volumes, important data disappears when containers are removed.&lt;/p&gt;

&lt;p&gt;Together, they form the foundation of modern containerized applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Running a single container is useful, but real-world applications require much more.&lt;/p&gt;

&lt;p&gt;A web application needs to communicate with databases, caches, and supporting services. At the same time, important data must survive container restarts, updates, and redeployments.&lt;/p&gt;

&lt;p&gt;Docker Networks solve communication challenges through service discovery and isolation.&lt;/p&gt;

&lt;p&gt;Docker Volumes solve persistence challenges by separating data from container lifecycles.&lt;/p&gt;

&lt;p&gt;These two concepts are fundamental building blocks for everything that comes next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Cloud-native applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Master Docker networking and volumes, and we'll be well prepared to build and operate real-world containerized applications.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>networking</category>
      <category>containers</category>
    </item>
    <item>
      <title>Serving Data Cloud Data to CRM-Only Users in Salesforce</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Thu, 13 Aug 2026 17:45:44 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/serving-data-cloud-data-to-crm-only-users-in-salesforce-cpi</link>
      <guid>https://dev.to/themdmohiuddin/serving-data-cloud-data-to-crm-only-users-in-salesforce-cpi</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to securely expose Salesforce Data Cloud data to CRM-only users&lt;br&gt;
without giving those users direct Data Cloud access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A common Salesforce architecture problem looks simple at first:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Lightning component needs to display Data Cloud data, but the users&lt;br&gt;
viewing the component are CRM-only users who don't have Data Cloud&lt;br&gt;
access.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, it seems like &lt;code&gt;ConnectApi.CdpQuery&lt;/code&gt; should solve the&lt;br&gt;
problem. Unfortunately, the query runs in the context of the browsing&lt;br&gt;
user. If that user doesn't have the required Data Cloud access, the&lt;br&gt;
query is rejected.&lt;/p&gt;

&lt;p&gt;This article walks through an alternative architecture using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Apex&lt;/li&gt;
&lt;li&gt;  Lightning Web Components (LWC)&lt;/li&gt;
&lt;li&gt;  Named Credentials&lt;/li&gt;
&lt;li&gt;  External Credentials&lt;/li&gt;
&lt;li&gt;  An External Client App&lt;/li&gt;
&lt;li&gt;  A dedicated integration identity&lt;/li&gt;
&lt;li&gt;  Custom Permissions&lt;/li&gt;
&lt;li&gt;  Public Groups&lt;/li&gt;
&lt;li&gt;  Apex-controlled row-level filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a component that can show the same Data Cloud dataset to&lt;br&gt;
CRM users while applying different row-level rules based on the&lt;br&gt;
Salesforce user's public-group membership.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine a Data Cloud data model object containing member plans:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Suppose the dataset contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EPO  → 5 rows
HMO  → 5 rows
POS  → 5 rows
PPO  → 5 rows
----------------
Total → 20 rows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine two CRM users:&lt;/p&gt;

&lt;p&gt;User     Data Cloud Access   Public Group             Expected Result&lt;/p&gt;




&lt;p&gt;User 1   No                  Not a member             All 20 plans&lt;br&gt;
  User 2   No                  &lt;code&gt;Member_Plan_HMO_Only&lt;/code&gt;   5 HMO plans&lt;/p&gt;

&lt;p&gt;Both users should be able to use the same Lightning component.&lt;/p&gt;

&lt;p&gt;However, neither user should authenticate directly to Data Cloud.&lt;/p&gt;

&lt;p&gt;That creates the core architecture challenge.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why &lt;code&gt;ConnectApi.CdpQuery&lt;/code&gt; Isn't Enough
&lt;/h2&gt;

&lt;p&gt;A natural first attempt is to query Data Cloud directly from Apex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;ConnectApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;CdpQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryDataCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that the query executes as the current Salesforce user.&lt;/p&gt;

&lt;p&gt;If User 2 is a CRM-only user without Data Cloud access, Data Cloud sees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User 2
   |
   v
Data Cloud Query
   |
   v
Access denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component cannot simply "borrow" another user's Data Cloud&lt;br&gt;
permissions.&lt;/p&gt;

&lt;p&gt;We therefore need a different execution identity.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Core Idea: Separate Execution Identity From Browsing User
&lt;/h2&gt;

&lt;p&gt;The architecture separates two concepts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browsing user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Salesforce CRM user who clicked the component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration identity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A dedicated Salesforce user whose credentials are used to access Data&lt;br&gt;
Cloud.&lt;/p&gt;

&lt;p&gt;The browser user never receives the integration user's credentials.&lt;/p&gt;

&lt;p&gt;Instead, Salesforce's Named Credential framework handles authentication&lt;br&gt;
for the outbound request.&lt;/p&gt;

&lt;p&gt;The resulting architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRM User
   |
   | Lightning Component
   v
Apex Controller
   |
   |-- Gate 1: Custom Permission
   |
   | Named Credential
   v
External Credential
   |
   | Client Credentials
   v
External Client App
   |
   | Run As
   v
Integration User
   |
   v
Data Cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us an important separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who is viewing the component?
        ↓
Salesforce CRM user

Who queries Data Cloud?
        ↓
Integration user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Important Trap
&lt;/h2&gt;

&lt;p&gt;There is an easy-to-miss permission boundary here.&lt;/p&gt;

&lt;p&gt;A Named Credential can authenticate the outbound call using a named&lt;br&gt;
principal, but the Apex transaction is still executing as the current&lt;br&gt;
Salesforce user.&lt;/p&gt;

&lt;p&gt;That means the browsing user must be permitted to use the stored&lt;br&gt;
External Credential principal.&lt;/p&gt;

&lt;p&gt;Without that grant, the callout can fail even when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The External Client App is configured correctly&lt;/li&gt;
&lt;li&gt;  The integration user has Data Cloud access&lt;/li&gt;
&lt;li&gt;  The Named Credential is configured correctly&lt;/li&gt;
&lt;li&gt;  The current user is an administrator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the most common reasons this architecture appears&lt;br&gt;
correctly configured but doesn't work.&lt;/p&gt;

&lt;p&gt;The flow therefore has &lt;strong&gt;two different permission gates&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;Gate 1
CRM User
   |
   | Custom Permission
   v
Can the user call the feature?

Gate 2
CRM User
   |
   | External Credential Principal Access
   v
Can the Apex transaction use the stored credential?

        ↓

Integration Identity
        ↓

Data Cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How the Identity Swap Works
&lt;/h2&gt;

&lt;p&gt;Two pieces make the identity swap possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Named Principal
&lt;/h3&gt;

&lt;p&gt;The External Credential uses a named principal.&lt;/p&gt;

&lt;p&gt;The authentication flow uses client credentials against a Salesforce&lt;br&gt;
External Client App whose &lt;strong&gt;Run As&lt;/strong&gt; user has the required Data Cloud&lt;br&gt;
access.&lt;/p&gt;

&lt;p&gt;The resulting token belongs to the integration identity.&lt;/p&gt;

&lt;p&gt;It does not belong to the CRM user who clicked the component.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The Core Salesforce Domain
&lt;/h3&gt;

&lt;p&gt;The Data Cloud Query API is accessed through the Salesforce core domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/services/data/v64.0/ssot/queryv2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Named Credential points to the Salesforce My Domain.&lt;/p&gt;

&lt;p&gt;The Apex code can therefore make one authenticated callout without&lt;br&gt;
asking the CRM user to authenticate separately to Data Cloud.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Request Flow
&lt;/h2&gt;

&lt;p&gt;The complete request looks like this:&lt;/p&gt;

&lt;p&gt;Step   What Happens&lt;/p&gt;



&lt;p&gt;1      User calls &lt;code&gt;getMemberPlans()&lt;/code&gt;&lt;br&gt;
  2      Apex checks &lt;code&gt;View_Member_Plan_Data&lt;/code&gt;&lt;br&gt;
  3      Apex calls the &lt;code&gt;DataCloud_Core&lt;/code&gt; Named Credential&lt;br&gt;
  4      Salesforce verifies External Credential Principal Access&lt;br&gt;
  5      Named Credential injects the integration identity&lt;br&gt;
  6      Data Cloud executes the query as the integration user&lt;br&gt;
  7      Apex applies CRM-side row-level rules&lt;br&gt;
  8      LWC displays the permitted rows&lt;/p&gt;

&lt;p&gt;The critical point is that Data Cloud sees the integration identity,&lt;br&gt;
while Apex still knows who the actual CRM user is.&lt;/p&gt;


&lt;h2&gt;
  
  
  Row-Level Access in Apex
&lt;/h2&gt;

&lt;p&gt;Because the integration user has broad access to the Data Cloud dataset,&lt;br&gt;
Data Cloud itself cannot distinguish User 1 from User 2.&lt;/p&gt;

&lt;p&gt;Therefore, row-level access must be enforced in Apex.&lt;/p&gt;

&lt;p&gt;For this example, a public group determines whether the user is&lt;br&gt;
restricted to HMO plans.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Member_Plan_HMO_Only
          |
          +---- User 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Apex controller determines the current user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and checks whether that user belongs to the restricted public group.&lt;/p&gt;

&lt;p&gt;The resulting behavior is:&lt;/p&gt;

&lt;p&gt;User     Group Membership   Query Scope&lt;/p&gt;




&lt;p&gt;User 1   Not in group       All plan types&lt;br&gt;
  User 2   In group           &lt;code&gt;HMO&lt;/code&gt; only&lt;/p&gt;


&lt;h2&gt;
  
  
  Keep the Restriction Server-Side
&lt;/h2&gt;

&lt;p&gt;One of the most important security principles in this design is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The client never decides which rows it is allowed to see.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The restriction is derived inside Apex.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;RESTRICTED_GROUP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Member_Plan_HMO_Only'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;RESTRICTED_PLAN_TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;HMO'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller derives the restriction from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than accepting a value from the LWC.&lt;/p&gt;

&lt;p&gt;That means the client cannot simply send:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;and bypass the restriction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Protecting the Query From Injection
&lt;/h2&gt;

&lt;p&gt;There is another important consideration.&lt;/p&gt;

&lt;p&gt;If the Data Cloud Query API accepts raw SQL and doesn't provide the same&lt;br&gt;
bind-variable experience you might expect from ordinary Apex SOQL, query&lt;br&gt;
construction needs additional protection.&lt;/p&gt;

&lt;p&gt;Never blindly concatenate user-controlled values into the query.&lt;/p&gt;

&lt;p&gt;For example, don't build a query like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;SELECT ... WHERE PlanType = &lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="n"&gt;userSuppliedValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, use strict allowlisting.&lt;/p&gt;

&lt;p&gt;The principle should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
  |
  v
Is it an allowed value?
  |
  +-- No --&amp;gt; Reject
  |
  +-- Yes
        |
        v
     Build query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the restricted plan type, the value should come from server-side&lt;br&gt;
constants rather than from the browser.&lt;/p&gt;

&lt;p&gt;The same principle applies to any optional filter such as &lt;code&gt;groupNumber&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Allowlist and reject. Don't sanitize and continue.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Setup Architecture
&lt;/h2&gt;

&lt;p&gt;The setup has several components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External Client App
        |
        | OAuth Client Credentials
        v
External Credential
        |
        | Named Principal
        v
Named Credential
        |
        v
Apex Controller
        |
        v
Data Cloud Query API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also two Salesforce-side access controls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Custom Permission
       +
External Credential Principal Access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together they determine whether a CRM user can invoke the feature and&lt;br&gt;
use the stored integration credential.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 --- Choose the Run-As User
&lt;/h2&gt;

&lt;p&gt;The Run-As user performs the Data Cloud queries.&lt;/p&gt;

&lt;p&gt;This user's access therefore becomes the effective Data Cloud access&lt;br&gt;
boundary for the integration.&lt;/p&gt;

&lt;p&gt;For a production implementation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer a dedicated integration user with only the Data Cloud access&lt;br&gt;
required by this feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Avoid using a highly privileged personal administrator account.&lt;/p&gt;

&lt;p&gt;A good principle is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Integration User
      |
      +-- Read required Data Cloud data
      +-- Nothing unnecessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This limits the blast radius if the integration is misconfigured.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 --- Create the External Client App
&lt;/h2&gt;

&lt;p&gt;In Salesforce Setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
  → External Client App Manager
  → New External Client App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Setting           Value&lt;/p&gt;




&lt;p&gt;Name              &lt;code&gt;Data Cloud MemberPlan Integration&lt;/code&gt;&lt;br&gt;
  Enable OAuth      Enabled&lt;br&gt;
  Callback URL      Salesforce OAuth success URL&lt;br&gt;
  Scopes            &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;cdp_query_api&lt;/code&gt;, &lt;code&gt;refresh_token&lt;/code&gt; / &lt;code&gt;offline_access&lt;/code&gt;&lt;br&gt;
  Flow Enablement   Client Credentials Flow&lt;/p&gt;

&lt;p&gt;Then configure OAuth policies.&lt;/p&gt;

&lt;p&gt;The important setting is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run As = Integration User
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client credentials flow uses this Run-As identity.&lt;/p&gt;

&lt;p&gt;After creating the app, obtain the:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Consumer Key
Consumer Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow Time for Propagation
&lt;/h3&gt;

&lt;p&gt;After creating or changing the External Client App, allow time for the&lt;br&gt;
configuration to propagate before troubleshooting authentication.&lt;/p&gt;

&lt;p&gt;An authentication failure immediately after configuration doesn't&lt;br&gt;
necessarily mean the credentials are wrong.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3 --- Create the External Credential
&lt;/h2&gt;

&lt;p&gt;Navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
  → Named Credentials
  → External Credentials
  → New
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Setting                   Value&lt;/p&gt;




&lt;p&gt;Label                     &lt;code&gt;DataCloud_IntegrationUser&lt;/code&gt;&lt;br&gt;
  Name                      &lt;code&gt;DataCloud_IntegrationUser&lt;/code&gt;&lt;br&gt;
  Authentication Protocol   OAuth 2.0&lt;br&gt;
  Flow Type                 Client Credentials with Client Secret&lt;br&gt;
  Identity Provider URL     &lt;code&gt;&amp;lt;My Domain&amp;gt;/services/oauth2/token&lt;/code&gt;&lt;br&gt;
  Scope                     Leave blank&lt;/p&gt;
&lt;h3&gt;
  
  
  Important: Leave Scope Blank
&lt;/h3&gt;

&lt;p&gt;For this architecture, don't populate the Scope field in the External&lt;br&gt;
Credential.&lt;/p&gt;

&lt;p&gt;The OAuth scopes are defined on the External Client App.&lt;/p&gt;

&lt;p&gt;If the token endpoint rejects a scope parameter in this flow,&lt;br&gt;
authentication can fail with an error such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;invalid_request
scope parameter not supported
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Create the External Credential Principal
&lt;/h2&gt;

&lt;p&gt;Under &lt;strong&gt;Principals&lt;/strong&gt;, create the named principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parameter Name:
DataCloudIntegration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then provide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Consumer Key
Consumer Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the principal and authenticate it.&lt;/p&gt;

&lt;p&gt;The principal should report:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;before continuing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 --- Create the Named Credential
&lt;/h2&gt;

&lt;p&gt;Create a Named Credential:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Label:
DataCloud Core

Name:
DataCloud_Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name is important because the Apex controller references it&lt;br&gt;
directly.&lt;/p&gt;

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

&lt;p&gt;Setting                         Value&lt;/p&gt;



&lt;p&gt;Label                           &lt;code&gt;DataCloud Core&lt;/code&gt;&lt;br&gt;
  Name                            &lt;code&gt;DataCloud_Core&lt;/code&gt;&lt;br&gt;
  URL                             &lt;code&gt;&amp;lt;My Domain&amp;gt;&lt;/code&gt;&lt;br&gt;
  Enabled for Callouts            Yes&lt;br&gt;
  External Credential             &lt;code&gt;DataCloud_IntegrationUser&lt;/code&gt;&lt;br&gt;
  Generate Authorization Header   Yes&lt;/p&gt;

&lt;p&gt;The Apex code can then reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;callout:DataCloud_Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Named Credential handles authentication instead of requiring&lt;br&gt;
credentials to be embedded in Apex.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 5 --- Grant External Credential Principal Access
&lt;/h2&gt;

&lt;p&gt;This is the step that's easiest to miss.&lt;/p&gt;

&lt;p&gt;The Apex transaction needs permission to use the stored External&lt;br&gt;
Credential principal.&lt;/p&gt;

&lt;p&gt;The permission set can contain an External Credential Principal Access&lt;br&gt;
entry similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;externalCredentialPrincipalAccesses&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;enabled&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enabled&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;externalCredentialPrincipal&amp;gt;&lt;/span&gt;
        DataCloud_IntegrationUser-DataCloudIntegration
    &lt;span class="nt"&gt;&amp;lt;/externalCredentialPrincipal&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/externalCredentialPrincipalAccesses&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value follows this pattern:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For this example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The names must match the actual org configuration.&lt;/p&gt;

&lt;p&gt;This access grant does &lt;strong&gt;not&lt;/strong&gt; give the CRM user Data Cloud access.&lt;/p&gt;

&lt;p&gt;It only allows the Apex transaction to use the stored credential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploying the Metadata
&lt;/h2&gt;

&lt;p&gt;Once the org-level configuration exists, deploy the Salesforce metadata.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf project deploy start &lt;span class="nt"&gt;-o&lt;/span&gt; dataCloud &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; force-app/main/default/classes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; force-app/main/default/lwc/memberPlanViewer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; force-app/main/default/customPermissions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; force-app/main/default/permissionsets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; force-app/main/default/groups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important sequencing rule is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External Client App
        ↓
External Credential
        ↓
Named Credential
        ↓
Metadata deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't deploy metadata that references an External Credential principal&lt;br&gt;
before that principal exists.&lt;/p&gt;


&lt;h2&gt;
  
  
  Verifying the Principal Name
&lt;/h2&gt;

&lt;p&gt;If you need to verify the External Credential principal in the org,&lt;br&gt;
query the metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf data query &lt;span class="nt"&gt;-o&lt;/span&gt; dataCloud &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SELECT ParameterName, ParameterType
      FROM ExternalCredentialParameter
      WHERE ExternalCredential.DeveloperName = 'DataCloud_IntegrationUser'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the Named Principal row when determining the principal name.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Tests
&lt;/h2&gt;

&lt;p&gt;The controller tests can be fully mocked, meaning they don't need live&lt;br&gt;
Data Cloud connectivity.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf apex run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; dataCloud &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; MemberPlanDataCloudControllerTest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-r&lt;/span&gt; human
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is important because your unit tests should verify the Apex&lt;br&gt;
behavior without depending on an external Data Cloud service.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 6 --- Assign the Permission Set
&lt;/h2&gt;

&lt;p&gt;The CRM user needs the permission set that grants access to the feature.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf org assign permset &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; dataCloud &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; Member_Plan_Viewer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--on-behalf-of&lt;/span&gt; &amp;lt;user2-username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone who executes the Apex controller, including an administrator&lt;br&gt;
testing the feature, must have the required permission set.&lt;/p&gt;


&lt;h2&gt;
  
  
  Configure the Restricted Public Group
&lt;/h2&gt;

&lt;p&gt;The public group controls the row-level restriction.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Add the users who should only see HMO plans.&lt;/p&gt;

&lt;p&gt;This membership is org data, not metadata.&lt;/p&gt;

&lt;p&gt;Therefore, it does not automatically deploy with your source code.&lt;/p&gt;

&lt;p&gt;You can manage membership through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
  → Public Groups
  → Member Plan HMO Only
  → Manage Members
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or through Salesforce CLI.&lt;/p&gt;

&lt;p&gt;The key rule is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User in group
    ↓
HMO only

User not in group
    ↓
All plans
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Place the Component on a Lightning Page
&lt;/h2&gt;

&lt;p&gt;Once the backend configuration is complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App Builder
    ↓
Lightning Page
    ↓
Member Plans (Data Cloud)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LWC calls the Apex controller.&lt;/p&gt;

&lt;p&gt;The user doesn't need to authenticate directly to Data Cloud.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verifying the Architecture
&lt;/h2&gt;

&lt;p&gt;Before running the complete demo, verify the authentication layer first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1 --- Verify the Integration Identity
&lt;/h3&gt;

&lt;p&gt;A simple callout can be used to inspect the identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;callout:DataCloud_Core/services/oauth2/userinfo'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;GET'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;LoggingLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important result is that the returned identity should be the&lt;br&gt;
configured Run-As user rather than the CRM user executing the Apex&lt;br&gt;
transaction.&lt;/p&gt;

&lt;p&gt;That demonstrates that the identity swap is working.&lt;/p&gt;


&lt;h2&gt;
  
  
  Layer 2 --- Test the Complete Chain
&lt;/h2&gt;

&lt;p&gt;Then call the actual controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;LoggingLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;MemberPlanDataCloudController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMemberPlans&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want to see something equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;success = true
rowCount &amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the complete path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRM User
   ↓
Custom Permission
   ↓
Principal Access
   ↓
Named Credential
   ↓
Integration Identity
   ↓
Data Cloud
   ↓
Apex Row Filtering
   ↓
LWC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Demo
&lt;/h2&gt;

&lt;p&gt;The most useful demonstration uses two CRM users.&lt;/p&gt;

&lt;h3&gt;
  
  
  User 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Permission:
Yes

Public Group:
No

Result:
20 rows

Plans:
EPO
HMO
POS
PPO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  User 2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Permission:
Yes

Public Group:
Member_Plan_HMO_Only

Result:
5 rows

Plans:
HMO only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important observation is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Both users are querying Data Cloud through the same integration&lt;br&gt;
identity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difference comes entirely from the CRM-side row-level rule.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Important Comparison
&lt;/h2&gt;

&lt;p&gt;If an existing &lt;code&gt;dataCloudAccountViewer&lt;/code&gt; component uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;ConnectApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;CdpQuery&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it runs as the browsing user.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRM-only User
     |
     v
ConnectApi.CdpQuery
     |
     v
Data Cloud
     |
     v
NO ACCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new architecture behaves differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRM-only User
     |
     v
Apex
     |
     v
Named Credential
     |
     v
Integration Identity
     |
     v
Data Cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That contrast demonstrates exactly why the Named Credential architecture&lt;br&gt;
is necessary.&lt;/p&gt;


&lt;h2&gt;
  
  
  Security: Read This Before Shipping
&lt;/h2&gt;

&lt;p&gt;This architecture changes where authorization happens.&lt;/p&gt;

&lt;p&gt;Data Cloud's normal per-user access model does not determine which rows&lt;br&gt;
this component returns once the query is executed under the integration&lt;br&gt;
identity.&lt;/p&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every access decision made by the component must be deliberately&lt;br&gt;
enforced in Salesforce Apex.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important controls are:&lt;/p&gt;

&lt;p&gt;Control                      Where It Is Enforced&lt;/p&gt;



&lt;p&gt;Who can call the feature     Custom Permission&lt;br&gt;
  Who can use the credential   External Credential Principal Access&lt;br&gt;
  Which rows a user can see    Public Group + Apex&lt;br&gt;
  Which columns are returned   Explicit SELECT&lt;br&gt;
  Filter safety                Strict allowlist&lt;br&gt;
  Data Cloud query identity    Integration User&lt;/p&gt;


&lt;h2&gt;
  
  
  Principle of Least Privilege
&lt;/h2&gt;

&lt;p&gt;The integration user is extremely important.&lt;/p&gt;

&lt;p&gt;The integration identity can potentially see everything the component&lt;br&gt;
queries.&lt;/p&gt;

&lt;p&gt;Therefore, don't treat it like an ordinary application user.&lt;/p&gt;

&lt;p&gt;In production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use a dedicated integration identity.&lt;/li&gt;
&lt;li&gt;  Grant only the required Data Cloud access.&lt;/li&gt;
&lt;li&gt;  Limit the dataset the application can query.&lt;/li&gt;
&lt;li&gt;  Keep the Apex authorization rules explicit.&lt;/li&gt;
&lt;li&gt;  Avoid giving the integration user unrelated privileges.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The integration user's permissions define the potential blast radius of&lt;br&gt;
the integration.&lt;/p&gt;


&lt;h2&gt;
  
  
  Never Trust Client-Supplied Authorization
&lt;/h2&gt;

&lt;p&gt;A dangerous design would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;getMemberPlans&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;planType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ALL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then allowing Apex to decide whether the user is permitted based on&lt;br&gt;
that value.&lt;/p&gt;

&lt;p&gt;The client should never determine authorization.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current Salesforce User
        |
        v
UserInfo.getUserId()
        |
        v
Check Public Group
        |
        v
Determine Scope
        |
        v
Build Safe Query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The authorization decision belongs on the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit Consideration
&lt;/h2&gt;

&lt;p&gt;There is an important trade-off with this architecture.&lt;/p&gt;

&lt;p&gt;Data Cloud audit trails will see the integration identity performing the&lt;br&gt;
query.&lt;/p&gt;

&lt;p&gt;They won't automatically identify the CRM user who originally clicked&lt;br&gt;
the component.&lt;/p&gt;

&lt;p&gt;If business auditing requires end-user attribution, log the CRM user&lt;br&gt;
separately on the Salesforce side.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can be included in an appropriate application audit record.&lt;/p&gt;

&lt;p&gt;This gives you two identities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data Cloud audit
    → Integration User

CRM application audit
    → Actual Salesforce User
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;When something goes wrong, identify which layer failed.&lt;/p&gt;




&lt;p&gt;Symptom                                   Likely Cause&lt;/p&gt;




&lt;p&gt;&lt;code&gt;We couldn't access the credential(s)…&lt;/code&gt;   External Credential Principal Access is&lt;br&gt;
                                            missing&lt;/p&gt;

&lt;p&gt;&lt;code&gt;View_Member_Plan_Data&lt;/code&gt; error             Permission set isn't assigned&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scope parameter not supported&lt;/code&gt;           External Credential Scope field is&lt;br&gt;
                                            populated&lt;/p&gt;

&lt;p&gt;&lt;code&gt;invalid_grant&lt;/code&gt; during authentication     Client Credentials Flow or Run-As&lt;br&gt;
                                            configuration is incorrect&lt;/p&gt;

&lt;p&gt;HTTP 401                                  Consumer credentials are wrong or the&lt;br&gt;
                                            client app hasn't propagated&lt;/p&gt;

&lt;p&gt;HTTP 403                                  Run-As user lacks Data Cloud access or&lt;br&gt;
                                            required OAuth scope&lt;/p&gt;

&lt;p&gt;HTTP 404                                  API version or Data Cloud provisioning&lt;br&gt;
                                            issue&lt;/p&gt;

&lt;p&gt;Query succeeds with 0 rows                Data Cloud dataset contains no matching&lt;br&gt;
                                            data&lt;/p&gt;

&lt;p&gt;Group member sees all plans               Public Group membership isn't configured&lt;br&gt;
                                            correctly&lt;/p&gt;

&lt;p&gt;Everyone sees HMO only                    Group membership or nested group&lt;br&gt;
                                            configuration is too broad&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note About HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;One detail is especially important for the Data Cloud Query API.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/ssot/queryv2&lt;/code&gt; endpoint can return:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;for a successful query.&lt;/p&gt;

&lt;p&gt;Don't assume every successful query must return HTTP 200.&lt;/p&gt;

&lt;p&gt;If your Apex query helper accepts both successful statuses, don't&lt;br&gt;
accidentally tighten the check to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and break valid Data Cloud responses.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Configuration Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Forgetting Principal Access
&lt;/h3&gt;

&lt;p&gt;Everything appears configured correctly, but every user gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We couldn't access the credential(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the permission set's External Credential Principal Access.&lt;/p&gt;




&lt;h3&gt;
  
  
  Mistake 2: Giving the CRM User Data Cloud Permissions
&lt;/h3&gt;

&lt;p&gt;The goal of this architecture is not to give CRM users direct Data Cloud&lt;br&gt;
access.&lt;/p&gt;

&lt;p&gt;The CRM user only needs the Salesforce permissions required to invoke&lt;br&gt;
the feature and use the stored credential.&lt;/p&gt;

&lt;p&gt;The integration identity handles Data Cloud access.&lt;/p&gt;


&lt;h3&gt;
  
  
  Mistake 3: Putting the OAuth Scope in the Wrong Place
&lt;/h3&gt;

&lt;p&gt;If the External Credential's Scope field is populated when the token&lt;br&gt;
endpoint doesn't accept that parameter, authentication can fail.&lt;/p&gt;

&lt;p&gt;Keep the configuration aligned with the OAuth flow supported by the&lt;br&gt;
External Client App.&lt;/p&gt;


&lt;h3&gt;
  
  
  Mistake 4: Using a Personal Admin as the Integration Identity
&lt;/h3&gt;

&lt;p&gt;This works for a demo but creates unnecessary risk in production.&lt;/p&gt;

&lt;p&gt;Use a dedicated integration identity with least privilege.&lt;/p&gt;


&lt;h3&gt;
  
  
  Mistake 5: Trusting LWC Parameters
&lt;/h3&gt;

&lt;p&gt;Never allow the browser to decide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which rows?
Which plan type?
Which user?
Which authorization level?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser is an untrusted boundary.&lt;/p&gt;

&lt;p&gt;Derive authorization from the Salesforce execution context.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Architecture
&lt;/h2&gt;

&lt;p&gt;Putting everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    CRM User
                       |
                       v
              Lightning Web Component
                       |
                       v
             MemberPlanDataCloudController
                       |
             +---------+---------+
             |                   |
             v                   v
     Custom Permission     Public Group
       Gate 1              Row Scope
             |                   |
             +---------+---------+
                       |
                       v
              Named Credential
                DataCloud_Core
                       |
                       v
             External Credential
          DataCloud_IntegrationUser
                       |
                       v
              External Client App
                       |
                       | Run As
                       v
                Integration User
                       |
                       v
                  Data Cloud
                       |
                       v
            ssot__MemberPlan__dlm
                       |
                       v
                Apex filtering
                       |
                       v
                     LWC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture provides a clear separation between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Authentication&lt;/li&gt;
&lt;li&gt;  Credential storage&lt;/li&gt;
&lt;li&gt;  Feature authorization&lt;/li&gt;
&lt;li&gt;  Row-level authorization&lt;/li&gt;
&lt;li&gt;  Data Cloud access&lt;/li&gt;
&lt;li&gt;  User experience&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When This Pattern Makes Sense
&lt;/h2&gt;

&lt;p&gt;This architecture is useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  CRM users need selected Data Cloud data.&lt;/li&gt;
&lt;li&gt;  Those users should not receive direct Data Cloud access.&lt;/li&gt;
&lt;li&gt;  A controlled integration identity can safely access the required
data.&lt;/li&gt;
&lt;li&gt;  Row-level access can be expressed and enforced in Apex.&lt;/li&gt;
&lt;li&gt;  The application needs a Lightning-based user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is especially useful for controlled internal applications where the&lt;br&gt;
Data Cloud dataset and authorization rules are well understood.&lt;/p&gt;


&lt;h2&gt;
  
  
  When to Be Careful
&lt;/h2&gt;

&lt;p&gt;This pattern should not be treated as a shortcut around Data Cloud&lt;br&gt;
security.&lt;/p&gt;

&lt;p&gt;If the integration identity can see sensitive information, your Apex&lt;br&gt;
layer becomes part of the security boundary.&lt;/p&gt;

&lt;p&gt;Before shipping, carefully review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Integration-user permissions&lt;/li&gt;
&lt;li&gt;  Apex authorization&lt;/li&gt;
&lt;li&gt;  Public-group membership&lt;/li&gt;
&lt;li&gt;  Query construction&lt;/li&gt;
&lt;li&gt;  Returned fields&lt;/li&gt;
&lt;li&gt;  Audit requirements&lt;/li&gt;
&lt;li&gt;  Error handling&lt;/li&gt;
&lt;li&gt;  Logging&lt;/li&gt;
&lt;li&gt;  Data classification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A credential swap doesn't eliminate authorization---it moves&lt;br&gt;
responsibility for certain access decisions into your application.&lt;/p&gt;


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

&lt;p&gt;The most important ideas are simple:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. The browsing user and query identity can be different
&lt;/h3&gt;

&lt;p&gt;The CRM user can use the component without directly authenticating to&lt;br&gt;
Data Cloud.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Named Credentials provide the authentication boundary
&lt;/h3&gt;

&lt;p&gt;The Named Credential supplies the integration identity for the outbound&lt;br&gt;
request.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Principal Access is a separate permission
&lt;/h3&gt;

&lt;p&gt;The executing Salesforce user must be allowed to use the stored External&lt;br&gt;
Credential principal.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Row-level authorization must be server-side
&lt;/h3&gt;

&lt;p&gt;If Data Cloud sees the integration identity, your Apex code must enforce&lt;br&gt;
the CRM user's row-level restrictions.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Never trust client input for authorization
&lt;/h3&gt;

&lt;p&gt;Derive access from the Salesforce execution context, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Use least privilege
&lt;/h3&gt;

&lt;p&gt;The integration identity should have only the Data Cloud access required&lt;br&gt;
by the application.&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Test each layer independently
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Credential
   ↓
Identity
   ↓
Data Cloud Query
   ↓
Apex Filtering
   ↓
LWC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before troubleshooting the entire chain at once.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Serving Data Cloud data to CRM-only users is less about finding a single&lt;br&gt;
magic API and more about understanding &lt;strong&gt;identity, authorization, and&lt;br&gt;
trust boundaries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key architectural decision is to separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who is using the application?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who is querying Data Cloud?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Named Credential and integration identity solve the authentication&lt;br&gt;
problem.&lt;/p&gt;

&lt;p&gt;A custom permission controls who can use the feature.&lt;/p&gt;

&lt;p&gt;A public group provides the business rule for row-level scope.&lt;/p&gt;

&lt;p&gt;Apex becomes the enforcement layer that connects all of them.&lt;/p&gt;

&lt;p&gt;The result is a controlled architecture where a CRM-only user can view&lt;br&gt;
Data Cloud information without being granted direct Data Cloud&lt;br&gt;
access---and different users can receive different data from the same&lt;br&gt;
component based on server-side authorization rules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Authentication tells you who is making the call. Authorization&lt;br&gt;
decides what they are allowed to see. In this architecture, keeping&lt;br&gt;
those two responsibilities separate is the key to making the design&lt;br&gt;
work safely.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>salesforce</category>
      <category>datacloud</category>
      <category>apex</category>
      <category>lwc</category>
    </item>
    <item>
      <title>Docker Commands Every DevOps Engineer Should Know</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Wed, 12 Aug 2026 11:29:29 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/docker-commands-every-devops-engineer-should-know-55bp</link>
      <guid>https://dev.to/themdmohiuddin/docker-commands-every-devops-engineer-should-know-55bp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learning Docker isn't just about running containers—it's about becoming fluent in the commands you'll use every day in real-world DevOps environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are the Docker commands you'll use repeatedly throughout your DevOps career for troubleshooting, debugging, monitoring, and managing containers. Think of this as the Docker equivalent of learning Linux commands like &lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and &lt;code&gt;tail&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Command Fluency Matters
&lt;/h2&gt;

&lt;p&gt;Anyone can copy and paste a &lt;code&gt;docker run&lt;/code&gt; command from documentation.&lt;/p&gt;

&lt;p&gt;The real skill begins when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A container suddenly stops working&lt;/li&gt;
&lt;li&gt;An application crashes after deployment&lt;/li&gt;
&lt;li&gt;You need to inspect logs&lt;/li&gt;
&lt;li&gt;You need to troubleshoot inside a running container&lt;/li&gt;
&lt;li&gt;Multiple containers are running simultaneously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these situations, command-line fluency becomes incredibly valuable.&lt;/p&gt;

&lt;p&gt;The commands in this article form the foundation of everyday Docker operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Viewing Running Containers with &lt;code&gt;docker ps&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The first command every Docker user should know is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This shows all currently running containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Variations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"status=exited"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What They Do
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all containers, including stopped ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show only container IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps --filter "status=exited"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show only stopped containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding Docker PS Output
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                  NAMES
a1b2c3d4e5f6   nginx     "/docker-entrypoint…"    5 minutes ago   Up 5 minutes   0.0.0.0:8080-&amp;gt;80/tcp   my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what each column means:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CONTAINER ID&lt;/td&gt;
&lt;td&gt;Unique identifier for the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IMAGE&lt;/td&gt;
&lt;td&gt;Image used to create the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COMMAND&lt;/td&gt;
&lt;td&gt;Process running inside the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STATUS&lt;/td&gt;
&lt;td&gt;Current container state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PORTS&lt;/td&gt;
&lt;td&gt;Port mappings between host and container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAMES&lt;/td&gt;
&lt;td&gt;Human-friendly container name&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;docker ps -a&lt;/code&gt; Is Important
&lt;/h3&gt;

&lt;p&gt;Suppose a container crashes.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;won't show it because it's no longer running.&lt;/p&gt;

&lt;p&gt;Instead use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;to find stopped containers and begin troubleshooting.&lt;/p&gt;

&lt;p&gt;This is often the first step when debugging container issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  Viewing Local Images with &lt;code&gt;docker images&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Containers run from images.&lt;/p&gt;

&lt;p&gt;To see which images exist on your machine:&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;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    a1b2c3d4e5f6   2 weeks ago   142MB
redis        latest    f6e5d4c3b2a1   3 weeks ago   117MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Columns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;REPOSITORY&lt;/td&gt;
&lt;td&gt;Image name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TAG&lt;/td&gt;
&lt;td&gt;Version label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IMAGE ID&lt;/td&gt;
&lt;td&gt;Unique image identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CREATED&lt;/td&gt;
&lt;td&gt;When the image was built&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIZE&lt;/td&gt;
&lt;td&gt;Storage size&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding Docker Tags
&lt;/h2&gt;

&lt;p&gt;Docker images use tags to identify versions.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull nginx:1.25
docker pull nginx:alpine
docker pull nginx:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why You Should Avoid &lt;code&gt;latest&lt;/code&gt; in Production
&lt;/h3&gt;

&lt;p&gt;Many beginners deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull nginx:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;latest&lt;/code&gt; changes over time.&lt;/p&gt;

&lt;p&gt;Tomorrow it might point to a completely different image.&lt;/p&gt;

&lt;p&gt;Instead, use explicit versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull nginx:1.25.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees consistent deployments and improves reproducibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Container Lifecycles
&lt;/h2&gt;

&lt;p&gt;Docker containers move through different states.&lt;/p&gt;

&lt;p&gt;The most common lifecycle commands are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop my-nginx
docker start my-nginx
docker restart my-nginx
docker &lt;span class="nb"&gt;kill &lt;/span&gt;my-nginx
docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Command Does
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Stop a Container
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Gracefully shuts down the container.&lt;/p&gt;




&lt;h4&gt;
  
  
  Start a Container
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Starts a previously stopped container.&lt;/p&gt;




&lt;h4&gt;
  
  
  Restart a Container
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Stops and starts the container in one command.&lt;/p&gt;




&lt;h4&gt;
  
  
  Force Kill a Container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;kill &lt;/span&gt;my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immediately terminates the container.&lt;/p&gt;

&lt;p&gt;Use this only when a normal stop fails.&lt;/p&gt;




&lt;h4&gt;
  
  
  Remove a Container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Permanently deletes a stopped container.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visualizing the Container Lifecycle
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run
     |
     v
  RUNNING
     |
docker stop
     |
     v
  STOPPED
     |
docker start
     |
     v
  RUNNING

docker rm
     |
     v
   DELETED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common misconception is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stopping a container deletes it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn't.&lt;/p&gt;

&lt;p&gt;A stopped container still exists.&lt;/p&gt;

&lt;p&gt;Only &lt;code&gt;docker rm&lt;/code&gt; permanently removes it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleaning Up Docker Resources
&lt;/h2&gt;

&lt;p&gt;As you practice Docker, you'll accumulate containers and unused resources.&lt;/p&gt;

&lt;p&gt;Docker provides cleanup commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove All Stopped Containers
&lt;/h3&gt;



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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Remove All Containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Clean Everything Unused
&lt;/h3&gt;



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

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Stopped containers&lt;/li&gt;
&lt;li&gt;Unused networks&lt;/li&gt;
&lt;li&gt;Dangling images&lt;/li&gt;
&lt;li&gt;Build cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a great command when you want a clean Docker environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Viewing Container Logs
&lt;/h2&gt;

&lt;p&gt;When containers misbehave, logs are your best friend.&lt;/p&gt;

&lt;p&gt;The command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;shows everything the container has written to standard output and standard error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Log Options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs my-nginx
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; my-nginx
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 50 my-nginx
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 10m my-nginx
docker logs &lt;span class="nt"&gt;-t&lt;/span&gt; my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Usage
&lt;/h3&gt;

&lt;p&gt;Follow logs in real time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works similarly to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; logfile.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;on Linux.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Logs Are So Important
&lt;/h2&gt;

&lt;p&gt;When a container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crashes&lt;/li&gt;
&lt;li&gt;Fails startup&lt;/li&gt;
&lt;li&gt;Returns errors&lt;/li&gt;
&lt;li&gt;Behaves unexpectedly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your first troubleshooting step should usually be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &amp;lt;container-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logs often contain the exact error message causing the issue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Entering a Running Container with &lt;code&gt;docker exec&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of Docker's most powerful commands is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-nginx /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens an interactive shell inside the container.&lt;/p&gt;

&lt;p&gt;Think of it as SSH for containers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Flags
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-nginx /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;-i&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Keeps standard input open.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;-t&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Allocates a terminal session.&lt;/p&gt;

&lt;p&gt;Together they create an interactive shell experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Some Containers Don't Have Bash
&lt;/h2&gt;

&lt;p&gt;Minimal images such as Alpine Linux often don't include Bash.&lt;/p&gt;

&lt;p&gt;If this fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; container-name /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; container-name sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead.&lt;/p&gt;

&lt;p&gt;This is completely normal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Commands Inside Containers
&lt;/h2&gt;

&lt;p&gt;Once inside a container, you can use standard Linux commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/nginx/nginx.conf
ps aux
&lt;span class="nb"&gt;pwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;The shell closes, but the container continues running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running Multiple Containers
&lt;/h2&gt;

&lt;p&gt;One of Docker's greatest strengths is isolation.&lt;/p&gt;

&lt;p&gt;You can run multiple services on the same machine.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my-nginx &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my-redis &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my-mongo &lt;span class="nt"&gt;-p&lt;/span&gt; 27017:27017 mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each service runs independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Port Conflicts
&lt;/h2&gt;

&lt;p&gt;Notice each container uses a different host port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8080 -&amp;gt; nginx
6379 -&amp;gt; redis
27017 -&amp;gt; mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker won't allow:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;because only one process can listen on a host port at a time.&lt;/p&gt;

&lt;p&gt;If you try, Docker will return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;port is already allocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Seeing Everything Running
&lt;/h2&gt;

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

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

&lt;/div&gt;



&lt;p&gt;to view all active containers.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTAINER ID   IMAGE
abc123         nginx
def456         redis
ghi789         mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms all services are running successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World DevOps Workflow
&lt;/h2&gt;

&lt;p&gt;A typical troubleshooting session might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
docker logs my-app
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-app sh
docker restart my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how these commands work together.&lt;/p&gt;

&lt;p&gt;You inspect.&lt;/p&gt;

&lt;p&gt;You investigate.&lt;/p&gt;

&lt;p&gt;You fix.&lt;/p&gt;

&lt;p&gt;You verify.&lt;/p&gt;

&lt;p&gt;This workflow becomes second nature over time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Docker containers are only useful if you know how to manage them effectively.&lt;/p&gt;

&lt;p&gt;The commands covered in this article are the foundation of everyday Docker operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker stop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker rm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker logs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker exec&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Master these commands and you'll be far more comfortable troubleshooting, debugging, and operating containerized applications.&lt;/p&gt;

&lt;p&gt;As you continue your DevOps journey, these commands will become muscle memory—and they'll prepare you for the next step: building your own Docker images and applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Great Docker users don't memorize commands. They understand how containers behave and know exactly which command to reach for when something goes wrong.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Docker Explained: Containers, Images &amp; Why They Changed Everything</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:30:52 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/docker-explained-containers-images-why-they-changed-everything-1306</link>
      <guid>https://dev.to/themdmohiuddin/docker-explained-containers-images-why-they-changed-everything-1306</guid>
      <description>&lt;p&gt;Over the last decade, Docker has transformed how software is built, shipped, and deployed. Today, almost every modern platform, cloud environment, CI/CD pipeline, and Kubernetes cluster relies on containers in some way.&lt;/p&gt;

&lt;p&gt;If you've ever heard developers say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It works on my machine."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Docker was built to solve exactly that problem.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What containers actually are&lt;/li&gt;
&lt;li&gt;How Docker works under the hood&lt;/li&gt;
&lt;li&gt;Images vs Containers&lt;/li&gt;
&lt;li&gt;Why containers are lighter than virtual machines&lt;/li&gt;
&lt;li&gt;Docker architecture explained&lt;/li&gt;
&lt;li&gt;Running your first Docker containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;




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

&lt;p&gt;Before Docker became mainstream, deploying applications was often frustrating.&lt;/p&gt;

&lt;p&gt;A developer might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10 installed locally&lt;/li&gt;
&lt;li&gt;Specific system libraries&lt;/li&gt;
&lt;li&gt;Certain environment variables&lt;/li&gt;
&lt;li&gt;Particular package versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything worked perfectly on their laptop.&lt;/p&gt;

&lt;p&gt;Then the same application would fail in staging or production because the environment was slightly different.&lt;/p&gt;

&lt;p&gt;This became known as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Works on my machine."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Docker solves this problem by packaging the application and everything it needs to run into a single portable unit.&lt;/p&gt;

&lt;p&gt;No matter where that container runs, the environment remains consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Container?
&lt;/h2&gt;

&lt;p&gt;A container is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A portable package that contains application code, dependencies, libraries, runtime components, and configuration required to run an application consistently across environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of it like a shipping container.&lt;/p&gt;

&lt;p&gt;The contents inside don't matter.&lt;/p&gt;

&lt;p&gt;As long as the container exists, it can be moved anywhere and still contain everything required.&lt;/p&gt;

&lt;p&gt;Software containers work the same way.&lt;/p&gt;

&lt;p&gt;A container can run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On a developer laptop&lt;/li&gt;
&lt;li&gt;In a test environment&lt;/li&gt;
&lt;li&gt;On a cloud server&lt;/li&gt;
&lt;li&gt;Inside Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without modification.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Linux Magic Behind Containers
&lt;/h2&gt;

&lt;p&gt;Docker didn't invent containers from scratch.&lt;/p&gt;

&lt;p&gt;It built upon powerful Linux kernel features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Namespaces
&lt;/h3&gt;

&lt;p&gt;Namespaces create isolation.&lt;/p&gt;

&lt;p&gt;Each container gets its own view of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processes&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;Filesystems&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From inside the container, it feels like its own machine.&lt;/p&gt;

&lt;p&gt;Even though multiple containers share the same host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Groups (cgroups)
&lt;/h3&gt;

&lt;p&gt;Control Groups (cgroups) manage resources.&lt;/p&gt;

&lt;p&gt;They allow Docker to control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Disk I/O&lt;/li&gt;
&lt;li&gt;Resource limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents one container from consuming all available resources.&lt;/p&gt;

&lt;p&gt;Together, namespaces and cgroups create the foundation of containerization.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Architecture Explained
&lt;/h2&gt;

&lt;p&gt;Many beginners think the Docker CLI does all the work.&lt;/p&gt;

&lt;p&gt;It doesn't.&lt;/p&gt;

&lt;p&gt;Docker uses a client-server architecture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker Client (CLI)
        |
        | API Request
        v
Docker Daemon (dockerd)
        |
        |-- Images
        |-- Containers
        |
        |-- Pull/Push
        v
     Docker Hub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Client
&lt;/h3&gt;

&lt;p&gt;The Docker Client is the command you type.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The client simply sends instructions to the Docker daemon.&lt;/p&gt;

&lt;p&gt;It does not run containers directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Daemon (dockerd)
&lt;/h3&gt;

&lt;p&gt;The Docker daemon is the engine behind Docker.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Building images&lt;/li&gt;
&lt;li&gt;Running containers&lt;/li&gt;
&lt;li&gt;Creating networks&lt;/li&gt;
&lt;li&gt;Managing storage volumes&lt;/li&gt;
&lt;li&gt;Communicating with registries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever you execute a Docker command, the daemon performs the actual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Images
&lt;/h3&gt;

&lt;p&gt;A Docker image is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A read-only blueprint used to create containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Images contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operating system layers&lt;/li&gt;
&lt;li&gt;Runtime dependencies&lt;/li&gt;
&lt;li&gt;Application code&lt;/li&gt;
&lt;li&gt;Startup configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An image is not running.&lt;/p&gt;

&lt;p&gt;It's simply a template.&lt;/p&gt;

&lt;p&gt;Think of it like a class in object-oriented programming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Containers
&lt;/h3&gt;

&lt;p&gt;A container is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A running instance of an image.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using the same image, you can launch multiple containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx image
   |
   |--&amp;gt; Container A
   |--&amp;gt; Container B
   |--&amp;gt; Container C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each container runs independently while sharing the same underlying image.&lt;/p&gt;

&lt;p&gt;Think of a container as an object created from a class.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Hub and Image Registries
&lt;/h2&gt;

&lt;p&gt;Docker images need a place to live.&lt;/p&gt;

&lt;p&gt;That's where registries come in.&lt;/p&gt;

&lt;p&gt;The most popular registry is Docker Hub.&lt;/p&gt;

&lt;p&gt;Common commands include:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Downloads an image.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Uploads an image.&lt;/p&gt;

&lt;p&gt;You can think of Docker Hub as GitHub for Docker images.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Docker Image Layers
&lt;/h2&gt;

&lt;p&gt;One of Docker's most powerful features is layered images.&lt;/p&gt;

&lt;p&gt;Each image consists of multiple read-only layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Writable Container Layer
------------------------
COPY app.py .
------------------------
RUN pip install ...
------------------------
FROM python:3.12-slim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Dockerfile instruction creates a new layer.&lt;/p&gt;

&lt;p&gt;When a container starts, Docker adds a writable layer on top.&lt;/p&gt;

&lt;p&gt;All runtime changes happen there.&lt;/p&gt;

&lt;p&gt;The original image remains unchanged.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Layers Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Faster Builds
&lt;/h3&gt;

&lt;p&gt;Docker caches layers.&lt;/p&gt;

&lt;p&gt;If only application code changes, Docker reuses dependency layers.&lt;/p&gt;

&lt;p&gt;This dramatically speeds up rebuilds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared Storage
&lt;/h3&gt;

&lt;p&gt;Suppose two images both use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python:3.12-slim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker stores that base layer only once.&lt;/p&gt;

&lt;p&gt;This saves disk space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disposable Containers
&lt;/h3&gt;

&lt;p&gt;Containers can be destroyed and recreated instantly.&lt;/p&gt;

&lt;p&gt;Because images never change, every new container starts from a clean state.&lt;/p&gt;

&lt;p&gt;This consistency is one of Docker's biggest strengths.&lt;/p&gt;




&lt;h2&gt;
  
  
  Containers vs Virtual Machines
&lt;/h2&gt;

&lt;p&gt;Many newcomers confuse containers with virtual machines.&lt;/p&gt;

&lt;p&gt;They solve similar problems differently.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Virtual Machine&lt;/th&gt;
&lt;th&gt;Docker Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Includes a full guest OS&lt;/td&gt;
&lt;td&gt;Shares host kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Larger in size&lt;/td&gt;
&lt;td&gt;Much smaller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slower startup&lt;/td&gt;
&lt;td&gt;Fast startup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong isolation&lt;/td&gt;
&lt;td&gt;Lightweight isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumes more resources&lt;/td&gt;
&lt;td&gt;More efficient&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Virtual Machine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
Guest OS
Hypervisor
Host OS
Hardware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
Libraries
Docker Engine
Host OS Kernel
Hardware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Containers share the host kernel.&lt;/p&gt;

&lt;p&gt;This is why they start in seconds instead of minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Windows and macOS
&lt;/h3&gt;

&lt;p&gt;Install Docker Desktop.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Docker CLI&lt;/li&gt;
&lt;li&gt;Docker Engine&lt;/li&gt;
&lt;li&gt;Docker Desktop UI&lt;/li&gt;
&lt;li&gt;Lightweight Linux VM&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;

&lt;p&gt;Install Docker Engine directly.&lt;/p&gt;

&lt;p&gt;Linux already provides the kernel Docker needs.&lt;/p&gt;

&lt;p&gt;No additional VM layer is required.&lt;/p&gt;

&lt;p&gt;Follow the installation guide for your distribution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your First Docker Container
&lt;/h2&gt;

&lt;p&gt;The classic first command is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;A lot happens behind the scenes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Docker checks for the image locally.&lt;/li&gt;
&lt;li&gt;If it doesn't exist, Docker downloads it.&lt;/li&gt;
&lt;li&gt;Docker creates a container.&lt;/li&gt;
&lt;li&gt;The container runs.&lt;/li&gt;
&lt;li&gt;The program exits successfully.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations.&lt;/p&gt;

&lt;p&gt;You just ran your first container.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running a Real Application
&lt;/h2&gt;

&lt;p&gt;Let's run Nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; my-nginx nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;-d&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Runs the container in detached mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;-p 8080:80&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Maps ports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host Port      Container Port
8080     --&amp;gt;        80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Traffic reaching your machine on port 8080 gets forwarded to Nginx running inside the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;--name my-nginx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Assigns a custom container name.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;nginx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Specifies the image to run.&lt;/p&gt;

&lt;p&gt;If it doesn't exist locally, Docker downloads it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Containers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
docker logs my-nginx
docker stop my-nginx
docker start my-nginx
docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see the Nginx welcome page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Docker Won
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Traditional Installation
&lt;/h3&gt;



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

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Linux server&lt;/li&gt;
&lt;li&gt;Package management&lt;/li&gt;
&lt;li&gt;System configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Docker Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Docker installed&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;No leftover configuration files.&lt;/p&gt;

&lt;p&gt;No package cleanup.&lt;/p&gt;

&lt;p&gt;No dependency conflicts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Docker didn't become popular because it was trendy.&lt;/p&gt;

&lt;p&gt;It became popular because it solved real engineering problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent environments&lt;/li&gt;
&lt;li&gt;Faster deployments&lt;/li&gt;
&lt;li&gt;Better resource utilization&lt;/li&gt;
&lt;li&gt;Easier scaling&lt;/li&gt;
&lt;li&gt;Simpler application packaging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Today Docker sits at the center of modern DevOps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI/CD pipelines build Docker images.&lt;/li&gt;
&lt;li&gt;Cloud platforms deploy Docker containers.&lt;/li&gt;
&lt;li&gt;Kubernetes orchestrates containers at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learning Docker isn't just learning another tool.&lt;/p&gt;

&lt;p&gt;It's learning the foundation of modern software delivery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Docker changed how software is shipped.&lt;/p&gt;

&lt;p&gt;By packaging applications together with their dependencies, containers eliminate environment inconsistencies and make deployments predictable.&lt;/p&gt;

&lt;p&gt;As you continue your DevOps journey, you'll discover that Docker is not the destination—it's the foundation.&lt;/p&gt;

&lt;p&gt;Everything from CI/CD pipelines to Kubernetes builds upon the concepts you've learned here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learn Docker deeply. It will pay dividends throughout your entire DevOps career.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Pull Requests, Code Reviews &amp; Git Rebase</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Sun, 09 Aug 2026 17:54:48 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/pull-requests-code-reviews-git-rebase-5fe1</link>
      <guid>https://dev.to/themdmohiuddin/pull-requests-code-reviews-git-rebase-5fe1</guid>
      <description>&lt;p&gt;If you've already learned Git commits, branching, and merging, you've mastered the mechanics of version control. But knowing Git alone doesn't make you effective on a real engineering team.&lt;/p&gt;

&lt;p&gt;In this article, we'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Pull Requests (PRs) actually are&lt;/li&gt;
&lt;li&gt;How professional code reviews work&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;.gitignore&lt;/code&gt; is essential&lt;/li&gt;
&lt;li&gt;The dangers of committing secrets&lt;/li&gt;
&lt;li&gt;When to use &lt;code&gt;git merge&lt;/code&gt; vs &lt;code&gt;git rebase&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the practices that transform a personal project into a professional software project.&lt;/p&gt;

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

&lt;p&gt;Imagine you're building a feature for a production application.&lt;/p&gt;

&lt;p&gt;You create a branch, write code, test it, and everything works.&lt;/p&gt;

&lt;p&gt;Should you push directly to &lt;code&gt;main&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;In most professional teams, the answer is &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead, your code goes through a review process where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Other developers inspect your changes&lt;/li&gt;
&lt;li&gt;Automated tests run&lt;/li&gt;
&lt;li&gt;Discussions happen around implementation decisions&lt;/li&gt;
&lt;li&gt;Quality checks prevent bugs from reaching production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That process starts with a &lt;strong&gt;Pull Request&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Pull Requests
&lt;/h2&gt;

&lt;p&gt;A Pull Request (PR) is &lt;strong&gt;not a Git feature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is a feature provided by platforms like GitHub, GitLab, and Bitbucket.&lt;/p&gt;

&lt;p&gt;A PR is essentially a request that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I've completed work on this branch. Please review it and merge it into the target branch."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What a Pull Request Contains
&lt;/h2&gt;

&lt;p&gt;A good PR provides much more than code.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Diff
&lt;/h3&gt;

&lt;p&gt;A visual representation of every change introduced by the branch.&lt;/p&gt;

&lt;p&gt;You'll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added lines&lt;/li&gt;
&lt;li&gt;Removed lines&lt;/li&gt;
&lt;li&gt;Modified files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows reviewers to inspect exactly what changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Description and Context
&lt;/h3&gt;

&lt;p&gt;A PR should explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What changed&lt;/li&gt;
&lt;li&gt;Why it changed&lt;/li&gt;
&lt;li&gt;How it was tested&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code shows what changed. The PR description explains why it changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Review Discussion
&lt;/h3&gt;

&lt;p&gt;Reviewers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment on specific lines&lt;/li&gt;
&lt;li&gt;Ask questions&lt;/li&gt;
&lt;li&gt;Suggest improvements&lt;/li&gt;
&lt;li&gt;Request changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a permanent record of engineering decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated Checks
&lt;/h3&gt;

&lt;p&gt;Most modern teams connect CI/CD pipelines to Pull Requests.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;Linting&lt;/li&gt;
&lt;li&gt;Security scans&lt;/li&gt;
&lt;li&gt;Build verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If these checks fail, the PR usually cannot be merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge Controls
&lt;/h3&gt;

&lt;p&gt;Once reviews are complete and checks pass, the PR can be merged.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the platform performs the merge operation for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Pull Requests Exist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quality Control
&lt;/h3&gt;

&lt;p&gt;Fresh eyes catch bugs.&lt;/p&gt;

&lt;p&gt;Reviewers often identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edge cases&lt;/li&gt;
&lt;li&gt;Logic flaws&lt;/li&gt;
&lt;li&gt;Performance concerns&lt;/li&gt;
&lt;li&gt;Security issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;before users ever encounter them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowledge Sharing
&lt;/h3&gt;

&lt;p&gt;Code reviews help teams stay informed about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New features&lt;/li&gt;
&lt;li&gt;Architecture decisions&lt;/li&gt;
&lt;li&gt;Business rules&lt;/li&gt;
&lt;li&gt;Coding standards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reviewing code is one of the fastest ways to learn a codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Historical Context
&lt;/h3&gt;

&lt;p&gt;Months later, someone may ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why was this implemented this way?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer often lives in the PR discussion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automation Gateway
&lt;/h3&gt;

&lt;p&gt;PRs provide a natural checkpoint for CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Before code reaches production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests run&lt;/li&gt;
&lt;li&gt;Builds run&lt;/li&gt;
&lt;li&gt;Security checks run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces deployment risk significantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Pull Request
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Feature Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/add-backup-script
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Make Changes and Commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add backup.sh

git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add automated backup script with cron scheduling"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Push the Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin feature/add-backup-script
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-u&lt;/code&gt; flag links your local branch to the remote branch so future pushes can simply use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Open the Pull Request
&lt;/h3&gt;

&lt;p&gt;On GitHub:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Compare &amp;amp; Pull Request&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select the target branch (&lt;code&gt;main&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Select your feature branch&lt;/li&gt;
&lt;li&gt;Add a title&lt;/li&gt;
&lt;li&gt;Write a meaningful description&lt;/li&gt;
&lt;li&gt;Submit the PR&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing Better PR Descriptions
&lt;/h2&gt;

&lt;p&gt;A strong PR description answers four questions:&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed?
&lt;/h3&gt;

&lt;p&gt;Brief summary of the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why was it needed?
&lt;/h3&gt;

&lt;p&gt;Business or technical motivation.&lt;/p&gt;

&lt;h3&gt;
  
  
  How was it tested?
&lt;/h3&gt;

&lt;p&gt;Evidence that the change works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should reviewers focus on?
&lt;/h3&gt;

&lt;p&gt;Any area that may need extra attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Reviews: How Professionals Review Code
&lt;/h2&gt;

&lt;p&gt;Code review is not about proving someone wrong.&lt;/p&gt;

&lt;p&gt;It's about improving the software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus on the Code, Not the Person
&lt;/h3&gt;

&lt;p&gt;Avoid personal criticism.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You wrote this incorrectly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This function could be simplified by...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Separate Requirements from Suggestions
&lt;/h3&gt;

&lt;p&gt;Many teams use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;for non-blocking suggestions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask Questions
&lt;/h3&gt;

&lt;p&gt;Instead of making assumptions, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happens if this input is empty?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Questions encourage discussion and often reveal hidden edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Receiving Review Feedback
&lt;/h2&gt;

&lt;p&gt;Every developer receives feedback.&lt;/p&gt;

&lt;p&gt;Good engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to comments&lt;/li&gt;
&lt;li&gt;Explain decisions&lt;/li&gt;
&lt;li&gt;Make improvements&lt;/li&gt;
&lt;li&gt;Discuss disagreements respectfully&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code review is a conversation, not a battle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding .gitignore
&lt;/h2&gt;

&lt;p&gt;One of the most important files in any repository is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This file tells Git:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never track these files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why .gitignore Matters
&lt;/h2&gt;

&lt;p&gt;Without it, developers accidentally commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;Build artifacts&lt;/li&gt;
&lt;li&gt;Dependency folders&lt;/li&gt;
&lt;li&gt;Personal editor settings&lt;/li&gt;
&lt;li&gt;Operating system files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example .gitignore
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Secrets
*.pem
*.key
.env
id_rsa
id_ed25519

# Logs
*.log
logs/

# Dependencies
node_modules/
__pycache__/
venv/

# IDE Files
.vscode/
.idea/

# Terraform
*.tfstate
*.tfstate.backup
.terraform/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Never Commit Secrets
&lt;/h2&gt;

&lt;p&gt;This is one of the most important rules in software engineering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Deleting Later Doesn't Work
&lt;/h3&gt;

&lt;p&gt;If a secret appears in Git history, it remains in history even after being removed from later commits.&lt;/p&gt;

&lt;p&gt;Anyone with repository access can still retrieve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens When Secrets Leak?
&lt;/h3&gt;

&lt;p&gt;Automated bots constantly scan public repositories for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Database credentials&lt;/li&gt;
&lt;li&gt;AWS access keys&lt;/li&gt;
&lt;li&gt;Tokens&lt;/li&gt;
&lt;li&gt;Private keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Often within minutes of a push.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevention Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;.gitignore&lt;/code&gt; before starting a project&lt;/li&gt;
&lt;li&gt;Use environment variables&lt;/li&gt;
&lt;li&gt;Never hardcode credentials&lt;/li&gt;
&lt;li&gt;Review changes before committing&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;/div&gt;



&lt;p&gt;and ideally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before every commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git Rebase: An Alternative to Merge
&lt;/h2&gt;

&lt;p&gt;Most developers learn merging first.&lt;/p&gt;

&lt;p&gt;But Git also provides:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch feature/my-work

git rebase main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replays your commits on top of the latest version of &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before Rebase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:      A --- B --- C
                         \
feature:                  D --- E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After Rebase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:      A --- B --- C
                         \
feature:                   D' --- E'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git creates entirely new commits with new hashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Merge vs Rebase
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Merge&lt;/th&gt;
&lt;th&gt;Rebase&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Preserves exact history&lt;/td&gt;
&lt;td&gt;Creates linear history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adds merge commits&lt;/td&gt;
&lt;td&gt;Avoids merge commits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe for shared branches&lt;/td&gt;
&lt;td&gt;Rewrites history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Great for team collaboration&lt;/td&gt;
&lt;td&gt;Great for cleaning personal branches&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Golden Rule of Rebase
&lt;/h2&gt;

&lt;p&gt;Never rebase a branch that other developers are already using.&lt;/p&gt;

&lt;p&gt;A simple rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rebase your own branches. Merge shared branches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Learning Git commands is only the beginning.&lt;/p&gt;

&lt;p&gt;Professional software development requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull Requests&lt;/li&gt;
&lt;li&gt;Code Reviews&lt;/li&gt;
&lt;li&gt;Secure Git practices&lt;/li&gt;
&lt;li&gt;Proper &lt;code&gt;.gitignore&lt;/code&gt; usage&lt;/li&gt;
&lt;li&gt;Understanding when to merge and when to rebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These skills help teams collaborate effectively, maintain quality, and deploy software with confidence.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Great developers don't just write code. They create systems that make collaboration, quality, and safety easier for everyone on the team.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Git Branching &amp; Merging: The Foundation of Team Collaboration in Git</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Thu, 06 Aug 2026 16:57:04 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/git-branching-merging-the-foundation-of-team-collaboration-in-git-388k</link>
      <guid>https://dev.to/themdmohiuddin/git-branching-merging-the-foundation-of-team-collaboration-in-git-388k</guid>
      <description>&lt;p&gt;Git becomes truly powerful when multiple developers can work on different features at the same time without interfering with each other's work. That's exactly what branching and merging solve.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore how Git branches work, how to merge changes safely, resolve conflicts, collaborate through GitHub, and understand the workflow used by modern development teams.&lt;/p&gt;




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

&lt;p&gt;Imagine a team working on a web application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One developer is building a login page.&lt;/li&gt;
&lt;li&gt;Another is fixing a payment bug.&lt;/li&gt;
&lt;li&gt;A third is improving performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everyone worked directly on the same branch, changes would constantly collide, making development chaotic and risky.&lt;/p&gt;

&lt;p&gt;Git branching allows developers to work independently in isolated environments and merge their changes only when they're ready.&lt;/p&gt;

&lt;p&gt;This is the foundation of modern software collaboration.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Git Branch?
&lt;/h2&gt;

&lt;p&gt;Many beginners think a branch is a complete copy of a project.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;A Git branch is simply a lightweight pointer to a commit.&lt;/p&gt;

&lt;p&gt;Consider a project history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A --- B --- C
              ^
            main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;main&lt;/code&gt; points to the latest commit (&lt;code&gt;C&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When you create a new branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A --- B --- C
              ^
            main
              ^
        feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git creates another pointer.&lt;/p&gt;

&lt;p&gt;No files are copied.&lt;/p&gt;

&lt;p&gt;No duplicate project is created.&lt;/p&gt;

&lt;p&gt;Just another reference to the same commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Branches Grow
&lt;/h2&gt;

&lt;p&gt;Once you switch to the new branch and start making commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:    A --- B --- C
                        ^
                      main

feature-login:
                    D --- E
                          ^
                    feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the feature branch moves forward.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; branch remains unchanged.&lt;/p&gt;

&lt;p&gt;This allows you to experiment, develop features, and fix bugs without affecting the stable codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Git Branches Are So Fast
&lt;/h2&gt;

&lt;p&gt;Since a branch is only a pointer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a branch takes milliseconds.&lt;/li&gt;
&lt;li&gt;Switching branches is extremely fast.&lt;/li&gt;
&lt;li&gt;Large repositories don't become slower when creating branches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike older version control systems where branching was expensive, Git encourages creating branches frequently.&lt;/p&gt;

&lt;p&gt;Creating a branch for every feature or bug fix is considered a best practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating and Switching Branches
&lt;/h2&gt;

&lt;h3&gt;
  
  
  View Existing Branches
&lt;/h3&gt;



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

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; indicates the current branch.&lt;/p&gt;




&lt;h3&gt;
  
  
  Create a New Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the branch but does not switch to it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Switch to a Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch feature-login
&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;git checkout feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Create and Switch in One Command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Older equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Delete a Branch
&lt;/h3&gt;

&lt;p&gt;Safe delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Force delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-D&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use force deletion carefully because it can remove unmerged work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Branch Naming Best Practices
&lt;/h2&gt;

&lt;p&gt;Professional teams typically follow naming conventions.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feature/PROJ-123-user-authentication

bugfix/PROJ-456-fix-login-error

hotfix/critical-payment-issue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to understand purpose&lt;/li&gt;
&lt;li&gt;Connected to Jira tickets&lt;/li&gt;
&lt;li&gt;Easier code reviews&lt;/li&gt;
&lt;li&gt;Better repository organization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Understanding Git Merge
&lt;/h2&gt;

&lt;p&gt;Once your work is complete, you'll want to bring it back into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First switch to the target branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then merge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git now combines both histories.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fast-Forward Merge
&lt;/h2&gt;

&lt;p&gt;This happens when no new commits exist on &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before merge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:
A --- B --- C

feature:
A --- B --- C --- D --- E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A --- B --- C --- D --- E
                              ^
                      main &amp;amp; feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git simply moves the &lt;code&gt;main&lt;/code&gt; pointer forward.&lt;/p&gt;

&lt;p&gt;No special merge commit is created.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;Fast-Forward Merge&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three-Way Merge
&lt;/h2&gt;

&lt;p&gt;Real projects usually look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:
A --- B --- C

feature:
      \
       D --- E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While you were working, someone else added commits to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Git must now combine both histories.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A --- B --- C ----------- M
          \             /
           D --- E ----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;M&lt;/code&gt; is a merge commit.&lt;/p&gt;

&lt;p&gt;It contains two parent commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One from &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;One from &lt;code&gt;feature&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process is called a &lt;strong&gt;Three-Way Merge&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Merge Conflict?
&lt;/h2&gt;

&lt;p&gt;Sometimes Git can't determine which change should win.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Main Branch
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome to our awesome application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both modified the same line.&lt;/p&gt;

&lt;p&gt;Git stops and asks for help.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;Merge Conflict&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a Conflict Looks Like
&lt;/h2&gt;

&lt;p&gt;Git inserts markers directly into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Welcome to our application
=======
Welcome to our awesome application
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Markers
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Current branch version.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Separator.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Incoming branch version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resolving Merge Conflicts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Open the File
&lt;/h3&gt;

&lt;p&gt;Review both versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Decide the Final Content
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome to our awesome application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Remove Conflict Markers
&lt;/h3&gt;

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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Stage the Resolved File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add app.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Complete the Merge
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Git automatically generates a merge message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't Fear Merge Conflicts
&lt;/h2&gt;

&lt;p&gt;Many beginners panic when they see conflicts.&lt;/p&gt;

&lt;p&gt;Don't.&lt;/p&gt;

&lt;p&gt;Merge conflicts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal&lt;/li&gt;
&lt;li&gt;Expected&lt;/li&gt;
&lt;li&gt;Common in every team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even senior engineers resolve conflicts regularly.&lt;/p&gt;

&lt;p&gt;The skill isn't avoiding them.&lt;/p&gt;

&lt;p&gt;The skill is understanding and resolving them confidently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Remote Repositories
&lt;/h2&gt;

&lt;p&gt;Everything so far has been local.&lt;/p&gt;

&lt;p&gt;To collaborate, we use remote repositories hosted on platforms like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;GitLab&lt;/li&gt;
&lt;li&gt;Bitbucket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most common remote name is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Pushing Changes to GitHub
&lt;/h2&gt;

&lt;p&gt;Upload local commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends your local commits to GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pulling Changes From GitHub
&lt;/h2&gt;

&lt;p&gt;Download and merge changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps your local branch up to date.&lt;/p&gt;




&lt;h2&gt;
  
  
  What &lt;code&gt;git pull&lt;/code&gt; Actually Does
&lt;/h2&gt;

&lt;p&gt;Many developers use &lt;code&gt;git pull&lt;/code&gt; daily without knowing what it really does.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git pull
=
git fetch
+
git merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fetch
&lt;/h3&gt;

&lt;p&gt;Downloads new commits.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;No files are changed.&lt;/p&gt;

&lt;p&gt;No merge occurs.&lt;/p&gt;

&lt;p&gt;Git simply updates its knowledge of the remote repository.&lt;/p&gt;




&lt;h3&gt;
  
  
  Merge
&lt;/h3&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Git combines the changes into your current branch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Pull Before Push?
&lt;/h2&gt;

&lt;p&gt;Imagine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You create new commits.&lt;/li&gt;
&lt;li&gt;A teammate pushes changes first.&lt;/li&gt;
&lt;li&gt;You try pushing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Git rejects it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;! [rejected] main -&amp;gt; main (fetch first)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git is protecting shared history.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolve conflicts if necessary.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Understanding GitHub Flow
&lt;/h2&gt;

&lt;p&gt;GitHub Flow is one of the most widely used collaboration workflows.&lt;/p&gt;

&lt;p&gt;It is simple, lightweight, and works perfectly with CI/CD pipelines.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Create a Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Make Commits
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add login page"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Push the Branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This backs up your work and makes it visible to teammates.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Open a Pull Request (PR)
&lt;/h3&gt;

&lt;p&gt;A Pull Request is a request to merge your branch into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Discussion&lt;/li&gt;
&lt;li&gt;Review&lt;/li&gt;
&lt;li&gt;Feedback&lt;/li&gt;
&lt;li&gt;Collaboration&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 5: Code Review
&lt;/h3&gt;

&lt;p&gt;Teammates review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code quality&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Bugs&lt;/li&gt;
&lt;li&gt;Security concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suggestions are discussed before merging.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 6: Automated Checks Run
&lt;/h3&gt;

&lt;p&gt;Modern CI/CD pipelines automatically run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Linting&lt;/li&gt;
&lt;li&gt;Security scans&lt;/li&gt;
&lt;li&gt;Build verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only passing code can be merged.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 7: Merge Into Main
&lt;/h3&gt;

&lt;p&gt;Once approved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feature-login → main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes become part of the main codebase.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 8: Deploy
&lt;/h3&gt;

&lt;p&gt;Many organizations automatically deploy after merging into &lt;code&gt;main&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;Merge → CI/CD Pipeline → Deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 9: Delete the Branch
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The branch has served its purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visualizing GitHub Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:
A --- B ----------------------- M
          \                   /
           C --- D --- E ----
                feature-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create branch&lt;/li&gt;
&lt;li&gt;Commit changes&lt;/li&gt;
&lt;li&gt;Push branch&lt;/li&gt;
&lt;li&gt;Open PR&lt;/li&gt;
&lt;li&gt;Review&lt;/li&gt;
&lt;li&gt;Run automated checks&lt;/li&gt;
&lt;li&gt;Merge&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;li&gt;Delete branch&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why GitHub Flow Is So Popular
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simplicity
&lt;/h3&gt;

&lt;p&gt;Only one long-lived branch:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Everything else is temporary.&lt;/p&gt;




&lt;h3&gt;
  
  
  CI/CD Friendly
&lt;/h3&gt;

&lt;p&gt;Every Pull Request becomes a checkpoint for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Security scanning&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Encourages Code Reviews
&lt;/h3&gt;

&lt;p&gt;Changes reach production only after another engineer reviews them.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Quality&lt;/li&gt;
&lt;li&gt;Knowledge sharing&lt;/li&gt;
&lt;li&gt;Team collaboration&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;A branch is simply a movable pointer to a commit.&lt;/li&gt;
&lt;li&gt;Branches allow isolated development without affecting main.&lt;/li&gt;
&lt;li&gt;Merging combines work from multiple branches.&lt;/li&gt;
&lt;li&gt;Fast-forward merges occur when histories haven't diverged.&lt;/li&gt;
&lt;li&gt;Three-way merges create a merge commit when histories differ.&lt;/li&gt;
&lt;li&gt;Merge conflicts happen when Git can't decide between overlapping changes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git pull&lt;/code&gt; = &lt;code&gt;git fetch&lt;/code&gt; + &lt;code&gt;git merge&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git push&lt;/code&gt; shares your commits with a remote repository.&lt;/li&gt;
&lt;li&gt;GitHub Flow is the modern workflow used by most teams and integrates naturally with CI/CD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Master branching and merging, and you've learned the workflow that powers nearly every professional software team in the world.&lt;/p&gt;

&lt;p&gt;Happy Coding! 🚀&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Git Fundamentals: The Foundation of Modern DevOps</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Wed, 05 Aug 2026 15:45:38 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/git-fundamentals-the-foundation-of-modern-devops-525l</link>
      <guid>https://dev.to/themdmohiuddin/git-fundamentals-the-foundation-of-modern-devops-525l</guid>
      <description>&lt;p&gt;From CI/CD pipelines and Infrastructure as Code to Kubernetes deployments and application releases, almost every automation workflow begins with a change in a Git repository. A commit, push, merge, or tag can trigger an entire deployment pipeline automatically.&lt;/p&gt;

&lt;p&gt;Understanding Git isn't just about version control—it's about understanding the foundation that modern software delivery is built on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Git Matters in DevOps
&lt;/h2&gt;

&lt;p&gt;Think about the Software Development Life Cycle (SDLC):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan → Code → Build → Test → Release → Deploy → Operate → Monitor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Git sits at the center of this workflow.&lt;/p&gt;

&lt;p&gt;When code changes are committed and pushed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI/CD pipelines automatically start&lt;/li&gt;
&lt;li&gt;Tests run automatically&lt;/li&gt;
&lt;li&gt;Docker images get built&lt;/li&gt;
&lt;li&gt;Deployments can be triggered&lt;/li&gt;
&lt;li&gt;Infrastructure changes can be tracked&lt;/li&gt;
&lt;li&gt;Teams can collaborate safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In many organizations, Git becomes the single source of truth for both application code and infrastructure configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Git?
&lt;/h2&gt;

&lt;p&gt;Git is a &lt;strong&gt;Distributed Version Control System (DVCS)&lt;/strong&gt; that tracks changes to files over time.&lt;/p&gt;

&lt;p&gt;It allows developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track project history&lt;/li&gt;
&lt;li&gt;Revert mistakes&lt;/li&gt;
&lt;li&gt;Collaborate with teams&lt;/li&gt;
&lt;li&gt;Create isolated branches&lt;/li&gt;
&lt;li&gt;Maintain a complete record of changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike older centralized systems, Git is distributed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does "Distributed" Mean?
&lt;/h3&gt;

&lt;p&gt;Every copy of a Git repository contains the complete project history.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;You can work offline&lt;/li&gt;
&lt;li&gt;You can create commits without internet access&lt;/li&gt;
&lt;li&gt;Every clone acts as a backup&lt;/li&gt;
&lt;li&gt;There is no single point of failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you clone a repository, you're not just downloading files—you're downloading the entire project history.&lt;/p&gt;

&lt;p&gt;This is why platforms like GitHub, GitLab, and Bitbucket are not Git itself.&lt;/p&gt;

&lt;p&gt;Git is the version control system.&lt;/p&gt;

&lt;p&gt;GitHub and similar platforms simply provide a shared remote location where teams can collaborate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Git's Snapshot Model
&lt;/h2&gt;

&lt;p&gt;One of the most misunderstood concepts in Git is how it stores history.&lt;/p&gt;

&lt;p&gt;Many beginners assume Git stores only the differences between files.&lt;/p&gt;

&lt;p&gt;That's not how Git works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Stores Snapshots
&lt;/h3&gt;

&lt;p&gt;Every commit represents a complete snapshot of the project at a specific moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Commit A → Commit B → Commit C

Snapshot   Snapshot   Snapshot
of all     of all     of all
files      files      files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of each commit as a photograph of your project.&lt;/p&gt;

&lt;p&gt;When you return to a previous commit, Git simply restores that snapshot.&lt;/p&gt;

&lt;p&gt;This design makes operations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching branches&lt;/li&gt;
&lt;li&gt;Viewing history&lt;/li&gt;
&lt;li&gt;Restoring previous versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;extremely fast and efficient.&lt;/p&gt;

&lt;p&gt;Behind the scenes, Git avoids storing duplicate file data when files haven't changed, making storage efficient while maintaining the snapshot model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Areas of a Git Project
&lt;/h2&gt;

&lt;p&gt;To understand Git properly, you need to understand its three working areas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory
        |
     git add
        ↓
Staging Area
        |
   git commit
        ↓
Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Working Directory
&lt;/h3&gt;

&lt;p&gt;This is where you actively edit files.&lt;/p&gt;

&lt;p&gt;Any changes made here are considered &lt;strong&gt;unstaged changes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Staging Area
&lt;/h3&gt;

&lt;p&gt;The staging area acts as a preparation zone.&lt;/p&gt;

&lt;p&gt;You decide exactly which changes should be included in the next commit.&lt;/p&gt;

&lt;p&gt;This allows you to create clean and focused commits instead of committing everything at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Repository
&lt;/h3&gt;

&lt;p&gt;The repository contains committed snapshots and project history.&lt;/p&gt;

&lt;p&gt;Once changes are committed, they become part of Git's permanent history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Git Repository
&lt;/h2&gt;

&lt;p&gt;To start tracking a project with Git:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This command creates a hidden &lt;code&gt;.git&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.git&lt;/code&gt; folder stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit history&lt;/li&gt;
&lt;li&gt;Branch information&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Repository metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without the &lt;code&gt;.git&lt;/code&gt; folder, Git history does not exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Your Identity
&lt;/h3&gt;

&lt;p&gt;Git records who created each commit.&lt;/p&gt;

&lt;p&gt;Set your identity once per machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  git status: Your Most Important Command
&lt;/h2&gt;

&lt;p&gt;If there's one Git command you'll use constantly, it's:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This command tells you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which files have changed&lt;/li&gt;
&lt;li&gt;Which files are staged&lt;/li&gt;
&lt;li&gt;Which files are untracked&lt;/li&gt;
&lt;li&gt;What will happen next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever you're unsure about your repository's state:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;is the first command to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staging Changes with git add
&lt;/h2&gt;

&lt;p&gt;Before creating a commit, changes must be staged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage a Single File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stage Multiple Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add file1.txt file2.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stage Everything
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Interactive Staging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to choose specific portions of a file to stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be Careful with git add .
&lt;/h3&gt;

&lt;p&gt;While convenient, &lt;code&gt;git add .&lt;/code&gt; stages everything.&lt;/p&gt;

&lt;p&gt;Always verify what's staged:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;before creating a commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Snapshots with git commit
&lt;/h2&gt;

&lt;p&gt;Once changes are staged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add user authentication endpoint"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a permanent snapshot of the project.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-m&lt;/code&gt; flag allows you to write the commit message directly in the command.&lt;/p&gt;

&lt;p&gt;Without it, Git opens your default text editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing Better Commit Messages
&lt;/h2&gt;

&lt;p&gt;Good commit messages save time for both you and your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid Messages Like:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix
update
changes
test
final
final-final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prefer Messages Like:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add user authentication endpoint

fix: resolve null pointer exception in payment service

docs: update installation guide

refactor: simplify database connection logic

test: add unit tests for user service

chore: upgrade nginx package version

ci: add GitHub Actions deployment workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes history easier to read and understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Viewing Project History with git log
&lt;/h2&gt;

&lt;p&gt;Git provides several ways to inspect history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full History
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Displays:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit hash&lt;/li&gt;
&lt;li&gt;Author&lt;/li&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;li&gt;Commit message&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compact View
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a1b2c3d Add authentication
e4f5g6h Fix API validation
i7j8k9l Initial project setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Visual Branch Graph
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful when working with multiple branches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show Actual Code Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Displays the content changes for each commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filter by Author
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filter by File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--&lt;/span&gt; filename.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shows only commits that modified a specific file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Commit Hashes
&lt;/h2&gt;

&lt;p&gt;Every commit has a unique identifier called a commit hash.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The hash is generated from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit content&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;Parent commit information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even the smallest change creates a completely different hash.&lt;/p&gt;

&lt;p&gt;In practice, you'll usually use only the first few characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout a1b2c3d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git can usually identify the correct commit from a short unique prefix.&lt;/p&gt;

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

&lt;p&gt;Git is much more than a tool for saving code.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The foundation of CI/CD pipelines&lt;/li&gt;
&lt;li&gt;The source of truth for modern infrastructure&lt;/li&gt;
&lt;li&gt;The backbone of collaborative software development&lt;/li&gt;
&lt;li&gt;The trigger behind most DevOps automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before learning branches, pull requests, Docker, Kubernetes, or Terraform, it's important to understand how Git stores history and manages changes.&lt;/p&gt;

&lt;p&gt;Master these fundamentals, and every DevOps tool that follows will make much more sense.&lt;/p&gt;

&lt;p&gt;Happy learning! 🚀&lt;/p&gt;

</description>
      <category>devops</category>
      <category>git</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Virtualization &amp; Containers — Understanding the Technology That Made Cloud and Docker Possible</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Tue, 04 Aug 2026 16:04:30 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/virtualization-containers-understanding-the-technology-that-made-cloud-and-docker-possible-1h4a</link>
      <guid>https://dev.to/themdmohiuddin/virtualization-containers-understanding-the-technology-that-made-cloud-and-docker-possible-1h4a</guid>
      <description>&lt;p&gt;Before diving into Git, Docker, and Kubernetes, it's worth understanding the technology that made modern cloud computing possible: &lt;strong&gt;Virtualization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;More importantly, understanding virtualization makes it much easier to understand &lt;strong&gt;why containers became so popular&lt;/strong&gt; and why Docker transformed modern software deployment.&lt;/p&gt;




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

&lt;p&gt;Virtualization sits between traditional infrastructure and modern containerized environments.&lt;/p&gt;

&lt;p&gt;Without virtualization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud computing wouldn't exist in its current form&lt;/li&gt;
&lt;li&gt;AWS EC2 instances wouldn't be possible&lt;/li&gt;
&lt;li&gt;Running multiple servers on shared hardware would be difficult and expensive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding virtualization helps answer a fundamental question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did the industry move from Virtual Machines to Containers?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Virtualization?
&lt;/h2&gt;

&lt;p&gt;Virtualization is a technology that allows multiple isolated operating systems to run on a single physical machine.&lt;/p&gt;

&lt;p&gt;Instead of dedicating one physical server to one application, virtualization enables multiple independent systems to share the same hardware resources.&lt;/p&gt;

&lt;p&gt;The virtualization layer abstracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;Memory (RAM)&lt;/li&gt;
&lt;li&gt;Storage&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows several virtual environments to coexist while behaving as separate machines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before Virtualization
&lt;/h3&gt;

&lt;p&gt;A common approach was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One physical server&lt;/li&gt;
&lt;li&gt;One application&lt;/li&gt;
&lt;li&gt;Large amounts of unused resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Organizations often purchased many servers that spent most of their time underutilized.&lt;/p&gt;

&lt;h3&gt;
  
  
  After Virtualization
&lt;/h3&gt;

&lt;p&gt;One powerful physical machine can host multiple virtual servers, significantly improving hardware utilization and reducing infrastructure costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Virtual Machine (VM)?
&lt;/h2&gt;

&lt;p&gt;A Virtual Machine is a complete computer running inside another computer.&lt;/p&gt;

&lt;p&gt;Each VM contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A full operating system&lt;/li&gt;
&lt;li&gt;Its own kernel&lt;/li&gt;
&lt;li&gt;Virtual CPU&lt;/li&gt;
&lt;li&gt;Virtual memory&lt;/li&gt;
&lt;li&gt;Virtual disk&lt;/li&gt;
&lt;li&gt;Applications and services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the VM's perspective, it believes it's running on actual hardware.&lt;/p&gt;

&lt;p&gt;The layer that creates this illusion is called the &lt;strong&gt;Hypervisor&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Hypervisors
&lt;/h2&gt;

&lt;p&gt;A Hypervisor is responsible for creating, managing, and isolating Virtual Machines.&lt;/p&gt;

&lt;p&gt;It allocates physical resources and ensures one VM cannot interfere with another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type 1 Hypervisor (Bare Metal)
&lt;/h3&gt;

&lt;p&gt;Runs directly on physical hardware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────┐ ┌─────────┐ ┌─────────┐
│  VM 1   │ │  VM 2   │ │  VM 3   │
└─────────┘ └─────────┘ └─────────┘
┌─────────────────────────────┐
│      Type 1 Hypervisor      │
└─────────────────────────────┘
┌─────────────────────────────┐
│     Physical Hardware       │
└─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;VMware ESXi&lt;/li&gt;
&lt;li&gt;Hyper-V&lt;/li&gt;
&lt;li&gt;Xen&lt;/li&gt;
&lt;li&gt;AWS Infrastructure&lt;/li&gt;
&lt;li&gt;Google Cloud Infrastructure&lt;/li&gt;
&lt;li&gt;Azure Infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Benefits
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Better performance&lt;/li&gt;
&lt;li&gt;Lower overhead&lt;/li&gt;
&lt;li&gt;Designed for production environments&lt;/li&gt;
&lt;li&gt;Ideal for large-scale cloud platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type 2 Hypervisor (Hosted)
&lt;/h3&gt;

&lt;p&gt;Runs as an application on top of a host operating system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────┐ ┌─────────┐
│  VM 1   │ │  VM 2   │
└─────────┘ └─────────┘
┌─────────────────────┐
│  Type 2 Hypervisor  │
└─────────────────────┘
┌─────────────────────┐
│      Host OS        │
└─────────────────────┘
┌─────────────────────┐
│ Physical Hardware   │
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;VirtualBox&lt;/li&gt;
&lt;li&gt;VMware Workstation&lt;/li&gt;
&lt;li&gt;VMware Fusion&lt;/li&gt;
&lt;li&gt;Parallels&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Common Use Cases
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Learning environments&lt;/li&gt;
&lt;li&gt;Local development&lt;/li&gt;
&lt;li&gt;Testing different operating systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever run Ubuntu inside VirtualBox, you've already used a Type 2 Hypervisor.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Virtualization Was Revolutionary
&lt;/h2&gt;

&lt;p&gt;Virtualization solved several major infrastructure challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Resource Utilization
&lt;/h3&gt;

&lt;p&gt;Multiple servers can share the same hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolation
&lt;/h3&gt;

&lt;p&gt;Problems inside one VM typically don't affect others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster Provisioning
&lt;/h3&gt;

&lt;p&gt;Creating a VM takes minutes instead of waiting for new hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshots and Rollbacks
&lt;/h3&gt;

&lt;p&gt;Save a VM's state and restore it whenever needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Foundation of Cloud Computing
&lt;/h3&gt;

&lt;p&gt;Virtualization enabled cloud providers to securely offer virtual servers to millions of customers on shared infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Limitations of Virtual Machines
&lt;/h2&gt;

&lt;p&gt;Virtual Machines solved many problems, but they introduced new challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heavy Resource Usage
&lt;/h3&gt;

&lt;p&gt;Every VM requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its own operating system&lt;/li&gt;
&lt;li&gt;Its own kernel&lt;/li&gt;
&lt;li&gt;Background services&lt;/li&gt;
&lt;li&gt;System processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even before your application starts, significant resources are already consumed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slower Startup Times
&lt;/h3&gt;

&lt;p&gt;VMs must boot an entire operating system.&lt;/p&gt;

&lt;p&gt;Startup times often range from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 seconds&lt;/li&gt;
&lt;li&gt;Several minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Large Images
&lt;/h3&gt;

&lt;p&gt;VM images often measure in gigabytes.&lt;/p&gt;

&lt;p&gt;This makes them slower to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store&lt;/li&gt;
&lt;li&gt;Transfer&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Poor Fit for Microservices
&lt;/h3&gt;

&lt;p&gt;Modern applications frequently consist of dozens of small services.&lt;/p&gt;

&lt;p&gt;Running every service inside its own VM becomes expensive and inefficient.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Containers Changed Everything
&lt;/h2&gt;

&lt;p&gt;Containers solve the same problem as Virtual Machines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running isolated workloads on shared hardware.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The difference lies in &lt;em&gt;how&lt;/em&gt; isolation is achieved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Machines
&lt;/h3&gt;

&lt;p&gt;Every VM includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application&lt;/li&gt;
&lt;li&gt;Libraries&lt;/li&gt;
&lt;li&gt;Guest Operating System&lt;/li&gt;
&lt;li&gt;Guest Kernel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Containers
&lt;/h3&gt;

&lt;p&gt;Containers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application&lt;/li&gt;
&lt;li&gt;Required Libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they &lt;strong&gt;share the host operating system kernel&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Machines Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────┐ ┌─────┐ ┌─────┐
│ App │ │ App │ │ App │
├─────┤ ├─────┤ ├─────┤
│Bins/│ │Bins/│ │Bins/│
│Libs │ │Libs │ │Libs │
├─────┤ ├─────┤ ├─────┤
│Guest│ │Guest│ │Guest│
│ OS  │ │ OS  │ │ OS  │
└─────┘ └─────┘ └─────┘
┌─────────────────────┐
│     Hypervisor      │
└─────────────────────┘
┌─────────────────────┐
│ Physical Hardware   │
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Containers Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────┐ ┌─────┐ ┌─────┐
│ App │ │ App │ │ App │
├─────┤ ├─────┤ ├─────┤
│Bins/│ │Bins/│ │Bins/│
│Libs │ │Libs │ │Libs │
└─────┴─┴─────┴─┴─────┘
┌─────────────────────┐
│  Container Engine   │
│      Docker         │
└─────────────────────┘
┌─────────────────────┐
│  Shared Host Kernel │
└─────────────────────┘
┌─────────────────────┐
│ Physical Hardware   │
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Containers Became So Popular
&lt;/h2&gt;

&lt;p&gt;Because containers share the host kernel, they provide several advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster Startup
&lt;/h3&gt;

&lt;p&gt;Containers typically start in milliseconds or a few seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller Images
&lt;/h3&gt;

&lt;p&gt;Container images are often measured in megabytes rather than gigabytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Resource Efficiency
&lt;/h3&gt;

&lt;p&gt;No duplicate operating systems consuming RAM and CPU.&lt;/p&gt;

&lt;h3&gt;
  
  
  Higher Density
&lt;/h3&gt;

&lt;p&gt;One server can run dozens or hundreds of containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perfect for Microservices
&lt;/h3&gt;

&lt;p&gt;Containers make it easy to package, deploy, and scale individual services independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Containers vs Virtual Machines
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Virtual Machines&lt;/th&gt;
&lt;th&gt;Containers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Includes Full OS&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Includes Own Kernel&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup Speed&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;td&gt;Faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Usage&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Size&lt;/td&gt;
&lt;td&gt;Larger&lt;/td&gt;
&lt;td&gt;Smaller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Stronger&lt;/td&gt;
&lt;td&gt;Lighter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Density Per Server&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why Modern Infrastructure Uses Both
&lt;/h2&gt;

&lt;p&gt;In production environments, containers and VMs often work together.&lt;/p&gt;

&lt;p&gt;A common architecture looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Physical Server
       ↓
Virtual Machine
       ↓
Docker Containers
       ↓
Applications
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;VM-level isolation&lt;/li&gt;
&lt;li&gt;Container efficiency&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Stronger security boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly how many Kubernetes environments run today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking Ahead to Docker
&lt;/h2&gt;

&lt;p&gt;The concepts from today directly map to Docker.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Virtualization Concept&lt;/th&gt;
&lt;th&gt;Docker Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hypervisor&lt;/td&gt;
&lt;td&gt;Container Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VM Image&lt;/td&gt;
&lt;td&gt;Docker Image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running VM&lt;/td&gt;
&lt;td&gt;Running Container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guest OS&lt;/td&gt;
&lt;td&gt;Shared Host Kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VirtualBox&lt;/td&gt;
&lt;td&gt;Docker Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Understanding this mapping makes Docker significantly easier to learn.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Virtualization transformed infrastructure by allowing multiple servers to share the same hardware.&lt;/p&gt;

&lt;p&gt;Containers took that idea further by removing the need for a separate operating system in every workload.&lt;/p&gt;

&lt;p&gt;The result was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster deployments&lt;/li&gt;
&lt;li&gt;Lower resource consumption&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Cloud-native architectures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;

&lt;h1&gt;
  
  
  devops #linux #virtualization #containers #docker #cloudcomputing #aws #kubernetes
&lt;/h1&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>virtualization</category>
      <category>cloud</category>
    </item>
    <item>
      <title>🌐 HTTP, HTTPS, Load Balancers &amp; Firewalls: Networking Concepts Every DevOps Engineer Should Know</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:46:47 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/http-https-load-balancers-firewalls-networking-concepts-every-devops-engineer-should-know-31f7</link>
      <guid>https://dev.to/themdmohiuddin/http-https-load-balancers-firewalls-networking-concepts-every-devops-engineer-should-know-31f7</guid>
      <description>&lt;p&gt;Networking doesn't stop at IP addresses, DNS, and ports.&lt;/p&gt;

&lt;p&gt;Once a client successfully reaches a server, several other technologies come into play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP and HTTPS enable communication between clients and servers&lt;/li&gt;
&lt;li&gt;Load balancers distribute traffic across multiple servers&lt;/li&gt;
&lt;li&gt;Firewalls control who is allowed to access services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These concepts form the foundation of modern web applications, cloud infrastructure, Kubernetes environments, and production systems.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What HTTP is and how it works&lt;/li&gt;
&lt;li&gt;Common HTTP methods and status codes&lt;/li&gt;
&lt;li&gt;How HTTPS secures communication&lt;/li&gt;
&lt;li&gt;Why load balancers are essential&lt;/li&gt;
&lt;li&gt;How firewalls protect infrastructure&lt;/li&gt;
&lt;li&gt;Essential UFW commands for Linux servers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Is HTTP?
&lt;/h2&gt;

&lt;p&gt;HTTP (HyperText Transfer Protocol) is the communication protocol used by web browsers and servers.&lt;/p&gt;

&lt;p&gt;It follows a simple request-response model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A client sends a request.&lt;/li&gt;
&lt;li&gt;The server processes the request.&lt;/li&gt;
&lt;li&gt;The server returns a response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;HTTP is stateless, meaning every request is independent and the server doesn't automatically remember previous requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anatomy of an HTTP Request
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/search?q=devops&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www.google.com&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl/8.1.2&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An HTTP request consists of:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;Action to perform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path&lt;/td&gt;
&lt;td&gt;Resource being requested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version&lt;/td&gt;
&lt;td&gt;HTTP protocol version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headers&lt;/td&gt;
&lt;td&gt;Metadata about the request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Body&lt;/td&gt;
&lt;td&gt;Optional request data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Anatomy of an HTTP Response
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15258&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Status Line&lt;/td&gt;
&lt;td&gt;Status code and message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headers&lt;/td&gt;
&lt;td&gt;Metadata about the response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Body&lt;/td&gt;
&lt;td&gt;Actual content returned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Common HTTP Methods
&lt;/h2&gt;

&lt;p&gt;Different methods tell the server what action to perform.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Retrieve data&lt;/td&gt;
&lt;td&gt;Load a webpage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Create new data&lt;/td&gt;
&lt;td&gt;Submit a form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;Replace a resource&lt;/td&gt;
&lt;td&gt;Update a profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;Modify part of a resource&lt;/td&gt;
&lt;td&gt;Update an email address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;Remove a resource&lt;/td&gt;
&lt;td&gt;Delete an account&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;Status codes tell us the result of a request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Status Code Categories
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1xx&lt;/td&gt;
&lt;td&gt;Informational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2xx&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3xx&lt;/td&gt;
&lt;td&gt;Redirection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4xx&lt;/td&gt;
&lt;td&gt;Client Errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5xx&lt;/td&gt;
&lt;td&gt;Server Errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Common Status Codes Every DevOps Engineer Should Know
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;201&lt;/td&gt;
&lt;td&gt;Created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Permanent Redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;302&lt;/td&gt;
&lt;td&gt;Temporary Redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;Bad Request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;Unauthorized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;td&gt;Forbidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;404&lt;/td&gt;
&lt;td&gt;Not Found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;Too Many Requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;Internal Server Error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;Bad Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;503&lt;/td&gt;
&lt;td&gt;Service Unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;504&lt;/td&gt;
&lt;td&gt;Gateway Timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Why 502, 503, and 504 Matter
&lt;/h3&gt;

&lt;p&gt;These errors often indicate infrastructure issues rather than application bugs.&lt;/p&gt;

&lt;h4&gt;
  
  
  502 Bad Gateway
&lt;/h4&gt;

&lt;p&gt;A proxy or load balancer received an invalid response from the backend service.&lt;/p&gt;

&lt;p&gt;Common cause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend application crashed&lt;/li&gt;
&lt;li&gt;Service isn't running&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  503 Service Unavailable
&lt;/h4&gt;

&lt;p&gt;The server is temporarily unable to handle requests.&lt;/p&gt;

&lt;p&gt;Common cause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintenance&lt;/li&gt;
&lt;li&gt;Resource exhaustion&lt;/li&gt;
&lt;li&gt;Overloaded server&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  504 Gateway Timeout
&lt;/h4&gt;

&lt;p&gt;The backend service took too long to respond.&lt;/p&gt;

&lt;p&gt;Common cause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow database queries&lt;/li&gt;
&lt;li&gt;Hung application processes&lt;/li&gt;
&lt;li&gt;Network latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are among the most common production issues DevOps engineers troubleshoot.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is HTTPS?
&lt;/h2&gt;

&lt;p&gt;HTTPS is HTTP running over TLS (Transport Layer Security).&lt;/p&gt;

&lt;p&gt;The communication model remains exactly the same, but all data is encrypted before being transmitted.&lt;/p&gt;

&lt;p&gt;Without HTTPS, attackers could potentially view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passwords&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;li&gt;API tokens&lt;/li&gt;
&lt;li&gt;Personal information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;HTTPS protects data while it's traveling across networks.&lt;/p&gt;




&lt;h2&gt;
  
  
  How TLS Works
&lt;/h2&gt;

&lt;p&gt;A simplified TLS handshake looks like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Client Hello
&lt;/h3&gt;

&lt;p&gt;The client initiates a secure connection request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Server Certificate
&lt;/h3&gt;

&lt;p&gt;The server provides a digital certificate proving its identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Certificate Validation
&lt;/h3&gt;

&lt;p&gt;The client verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificate validity&lt;/li&gt;
&lt;li&gt;Domain ownership&lt;/li&gt;
&lt;li&gt;Trusted Certificate Authority (CA)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Key Exchange
&lt;/h3&gt;

&lt;p&gt;Client and server securely establish a shared encryption key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Encrypted Communication
&lt;/h3&gt;

&lt;p&gt;All subsequent HTTP traffic becomes encrypted.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is an SSL/TLS Certificate?
&lt;/h2&gt;

&lt;p&gt;A certificate binds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A domain name&lt;/li&gt;
&lt;li&gt;A public key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The certificate is digitally signed by a trusted Certificate Authority (CA).&lt;/p&gt;

&lt;p&gt;Popular providers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's Encrypt&lt;/li&gt;
&lt;li&gt;DigiCert&lt;/li&gt;
&lt;li&gt;Sectigo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a valid certificate, browsers display security warnings.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Load Balancer?
&lt;/h2&gt;

&lt;p&gt;A load balancer sits between clients and backend servers.&lt;/p&gt;

&lt;p&gt;Instead of sending all traffic to a single server, it distributes requests across multiple servers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌──────────────┐
Clients ────────► │ Load Balancer│
                  └──────┬───────┘
                         │
         ┌───────────────┼───────────────┐
         ▼               ▼               ▼

     Server 1       Server 2       Server 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Load Balancers Are Important
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Traffic is distributed across multiple servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  High Availability
&lt;/h3&gt;

&lt;p&gt;Failed servers are automatically removed from rotation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero-Downtime Deployments
&lt;/h3&gt;

&lt;p&gt;Applications can be updated one server at a time without affecting users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Load Balancing Algorithms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Round Robin
&lt;/h3&gt;

&lt;p&gt;Requests are distributed evenly in sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 1 → Server 1
Request 2 → Server 2
Request 3 → Server 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Least Connections
&lt;/h3&gt;

&lt;p&gt;Traffic goes to the server with the fewest active connections.&lt;/p&gt;

&lt;p&gt;Useful when workloads vary significantly.&lt;/p&gt;




&lt;h3&gt;
  
  
  IP Hash
&lt;/h3&gt;

&lt;p&gt;Traffic is routed based on the client's IP address.&lt;/p&gt;

&lt;p&gt;Useful for session persistence.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Firewall?
&lt;/h2&gt;

&lt;p&gt;A firewall controls incoming and outgoing network traffic using predefined rules.&lt;/p&gt;

&lt;p&gt;Its purpose is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow legitimate traffic&lt;/li&gt;
&lt;li&gt;Block unwanted traffic&lt;/li&gt;
&lt;li&gt;Reduce attack surfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firewalls can filter traffic based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ports&lt;/li&gt;
&lt;li&gt;Protocols&lt;/li&gt;
&lt;li&gt;Source IP addresses&lt;/li&gt;
&lt;li&gt;Destination IP addresses&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Default-Deny Security Principle
&lt;/h2&gt;

&lt;p&gt;A strong security practice is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Block Everything
↓
Allow Only What Is Needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach minimizes exposure and reduces risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Firewalls with UFW
&lt;/h2&gt;

&lt;p&gt;Ubuntu provides a user-friendly firewall tool called UFW (Uncomplicated Firewall).&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Firewall Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Enable Firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Disable Firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw disable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Set Default Policies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny incoming
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default allow outgoing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Allow Essential Services
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Allow SSH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 22/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow HTTP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow HTTPS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 443/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow a Specific IP Address
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 203.0.113.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Remove a Firewall Rule
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw delete allow 80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  View Detailed Firewall Rules
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Important Warning Before Enabling UFW
&lt;/h2&gt;

&lt;p&gt;If you're connected via SSH, always allow port 22 before enabling the firewall.&lt;/p&gt;

&lt;p&gt;Correct sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 22/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failing to do this can lock you out of your server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Groups vs UFW
&lt;/h2&gt;

&lt;p&gt;Cloud servers often have two firewall layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud Security Groups
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;AWS Security Groups&lt;/li&gt;
&lt;li&gt;Azure NSGs&lt;/li&gt;
&lt;li&gt;GCP Firewall Rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These operate outside the virtual machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  UFW / iptables
&lt;/h3&gt;

&lt;p&gt;These operate inside the Linux operating system.&lt;/p&gt;

&lt;p&gt;Using both provides stronger protection through defense in depth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bringing It All Together
&lt;/h2&gt;

&lt;p&gt;When you visit a secure website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DNS resolves the domain name.&lt;/li&gt;
&lt;li&gt;Your browser connects to the server's IP.&lt;/li&gt;
&lt;li&gt;Traffic is sent through port 443.&lt;/li&gt;
&lt;li&gt;TLS encrypts the connection.&lt;/li&gt;
&lt;li&gt;An HTTP request is sent.&lt;/li&gt;
&lt;li&gt;A load balancer may distribute the request.&lt;/li&gt;
&lt;li&gt;A backend server processes it.&lt;/li&gt;
&lt;li&gt;Firewalls ensure only allowed traffic reaches the service.&lt;/li&gt;
&lt;li&gt;The server returns an HTTP response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding this flow is essential for anyone working in DevOps, Cloud Engineering, Site Reliability Engineering, or System Administration.&lt;/p&gt;

&lt;p&gt;The deeper you understand networking fundamentals, the easier it becomes to troubleshoot real-world production systems.&lt;/p&gt;

&lt;p&gt;Happy Learning! 🚀&lt;/p&gt;

</description>
      <category>devops</category>
      <category>networking</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>🌐 Networking Fundamentals Every DevOps Engineer Should Know (IP, DNS &amp; Ports)</title>
      <dc:creator>Md Mohiuddin</dc:creator>
      <pubDate>Sun, 02 Aug 2026 08:18:44 +0000</pubDate>
      <link>https://dev.to/themdmohiuddin/networking-fundamentals-every-devops-engineer-should-know-ip-dns-ports-407l</link>
      <guid>https://dev.to/themdmohiuddin/networking-fundamentals-every-devops-engineer-should-know-ip-dns-ports-407l</guid>
      <description>&lt;p&gt;If you're learning DevOps, networking is not optional.&lt;/p&gt;

&lt;p&gt;Every SSH connection, web request, Docker container, Kubernetes service, and cloud resource relies on networking behind the scenes.&lt;/p&gt;

&lt;p&gt;When applications fail to communicate, deployments break, or services become unreachable, networking is often the first place engineers investigate.&lt;/p&gt;

&lt;p&gt;In this article, I'll cover:&lt;/p&gt;

&lt;p&gt;✅ What IP addresses are&lt;br&gt;
✅ Public vs Private IPs&lt;br&gt;
✅ Understanding Subnets and CIDR&lt;br&gt;
✅ How DNS works&lt;br&gt;
✅ Common DNS record types&lt;br&gt;
✅ Understanding Ports&lt;br&gt;
✅ The Client-Server Model&lt;br&gt;
✅ Useful networking commands for troubleshooting&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Networking Matters in DevOps
&lt;/h2&gt;

&lt;p&gt;Almost every modern DevOps tool depends on networking.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;SSH connections to servers&lt;/li&gt;
&lt;li&gt;Web applications communicating with databases&lt;/li&gt;
&lt;li&gt;Docker containers talking to each other&lt;/li&gt;
&lt;li&gt;Kubernetes services routing traffic&lt;/li&gt;
&lt;li&gt;CI/CD pipelines deploying applications&lt;/li&gt;
&lt;li&gt;Cloud services communicating across regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When something stops working, one of the first questions engineers ask is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this a networking problem?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Understanding networking helps you answer that question quickly.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is an IP Address?
&lt;/h2&gt;

&lt;p&gt;An IP (Internet Protocol) address is a unique identifier assigned to a device on a network.&lt;/p&gt;

&lt;p&gt;It allows devices to locate and communicate with each other.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;IPv4 addresses contain four numbers separated by dots.&lt;/p&gt;

&lt;p&gt;Each section ranges from:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Without IP addresses, computers wouldn't know where to send data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Public vs Private IP Addresses
&lt;/h2&gt;

&lt;p&gt;There are two major categories of IP addresses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Public IP
&lt;/h3&gt;

&lt;p&gt;A public IP address is reachable from anywhere on the internet.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Cloud servers&lt;/li&gt;
&lt;li&gt;Public websites&lt;/li&gt;
&lt;li&gt;Home internet routers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your AWS EC2 instance will typically have a public IP so you can SSH into it remotely.&lt;/p&gt;




&lt;h3&gt;
  
  
  Private IP
&lt;/h3&gt;

&lt;p&gt;Private IP addresses are only accessible within a local network.&lt;/p&gt;

&lt;p&gt;Common private ranges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.0.0.0 – 10.255.255.255

172.16.0.0 – 172.31.255.255

192.168.0.0 – 192.168.255.255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll frequently encounter these ranges when working with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS VPCs&lt;/li&gt;
&lt;li&gt;Docker networks&lt;/li&gt;
&lt;li&gt;Kubernetes clusters&lt;/li&gt;
&lt;li&gt;Internal company networks&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Localhost (Loopback Address)
&lt;/h2&gt;

&lt;p&gt;A special IP address always refers to the current machine:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll often see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;which resolves to:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This allows you to test applications running on your own computer without using an external network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Subnets
&lt;/h2&gt;

&lt;p&gt;As networks grow, they are divided into smaller logical sections called subnets.&lt;/p&gt;

&lt;p&gt;A subnet helps organize devices and control traffic efficiently.&lt;/p&gt;




&lt;h3&gt;
  
  
  CIDR Notation
&lt;/h3&gt;

&lt;p&gt;Subnets are usually written using CIDR notation:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/24&lt;/code&gt; indicates the network portion of the address.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;/24&lt;/code&gt; subnet provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;256 IP addresses
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



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

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

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CIDR&lt;/th&gt;
&lt;th&gt;Approx. Addresses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/16&lt;/td&gt;
&lt;td&gt;65,536&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/24&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/28&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You'll see CIDR blocks frequently when creating cloud networks and VPCs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is DNS?
&lt;/h2&gt;

&lt;p&gt;Humans prefer names.&lt;/p&gt;

&lt;p&gt;Computers prefer IP addresses.&lt;/p&gt;

&lt;p&gt;DNS (Domain Name System) translates domain names into IP addresses.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;becomes something like:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This allows your browser to find the correct server.&lt;/p&gt;




&lt;h2&gt;
  
  
  How DNS Resolution Works
&lt;/h2&gt;

&lt;p&gt;When you visit a website:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;the lookup process generally follows these steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Device
      ↓
DNS Resolver
      ↓
Root Servers
      ↓
TLD Servers (.com)
      ↓
Authoritative DNS Server
      ↓
IP Address Returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, your computer connects to the returned IP address.&lt;/p&gt;

&lt;p&gt;This entire process usually takes only milliseconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common DNS Record Types
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;Domain → IPv4 Address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AAAA&lt;/td&gt;
&lt;td&gt;Domain → IPv6 Address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;Domain Alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MX&lt;/td&gt;
&lt;td&gt;Mail Server Records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;Verification &amp;amp; Security Records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NS&lt;/td&gt;
&lt;td&gt;Name Server Information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a DevOps engineer, you'll regularly manage DNS records when deploying applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Ports
&lt;/h2&gt;

&lt;p&gt;An IP address identifies a machine.&lt;/p&gt;

&lt;p&gt;A port identifies a service running on that machine.&lt;/p&gt;

&lt;p&gt;Think of it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP Address = Building Address
Port = Apartment Number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple applications can run on the same server because each listens on a different port.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Ports Every DevOps Engineer Should Know
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3306&lt;/td&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5432&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6379&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27017&lt;/td&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.10:22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSH service&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Web server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.10:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secure website&lt;/p&gt;




&lt;h2&gt;
  
  
  The Client-Server Model
&lt;/h2&gt;

&lt;p&gt;Most applications follow the client-server model.&lt;/p&gt;

&lt;p&gt;A client sends a request.&lt;/p&gt;

&lt;p&gt;A server receives the request and returns a response.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLIENT                     SERVER

Browser  ───── Request ───► Nginx

Browser ◄──── Response ─── Nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of clients:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web browsers&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;li&gt;curl&lt;/li&gt;
&lt;li&gt;API consumers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples of servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Apache&lt;/li&gt;
&lt;li&gt;Node.js applications&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;/ul&gt;




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

&lt;h3&gt;
  
  
  ping
&lt;/h3&gt;

&lt;p&gt;Check whether a host is reachable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;64 bytes from 142.x.x.x: time=14 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic connectivity testing&lt;/li&gt;
&lt;li&gt;Measuring latency&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  nslookup
&lt;/h3&gt;

&lt;p&gt;Perform a DNS lookup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output shows the IP address associated with the domain.&lt;/p&gt;




&lt;h3&gt;
  
  
  dig
&lt;/h3&gt;

&lt;p&gt;A more advanced DNS troubleshooting tool.&lt;/p&gt;

&lt;p&gt;Basic lookup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig google.com +short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lookup MX records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig google.com MX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View the full DNS resolution path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +trace google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  curl
&lt;/h3&gt;

&lt;p&gt;Send HTTP requests directly from the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Show only response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verbose mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save output to a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; page.html https://google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;curl -I&lt;/code&gt; is one of the most useful troubleshooting commands in DevOps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Following the Journey of a Request
&lt;/h2&gt;

&lt;p&gt;Let's see what happens when you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step-by-step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DNS resolves &lt;code&gt;google.com&lt;/code&gt; to an IP address.&lt;/li&gt;
&lt;li&gt;Your machine connects to that IP.&lt;/li&gt;
&lt;li&gt;It uses port &lt;code&gt;443&lt;/code&gt; for HTTPS.&lt;/li&gt;
&lt;li&gt;A secure TLS connection is established.&lt;/li&gt;
&lt;li&gt;The HTTP request is sent.&lt;/li&gt;
&lt;li&gt;Google's server processes the request.&lt;/li&gt;
&lt;li&gt;An HTTP response is returned.&lt;/li&gt;
&lt;li&gt;curl displays the result.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DNS
 ↓
IP Address
 ↓
Port 443
 ↓
TLS Handshake
 ↓
HTTP Request
 ↓
HTTP Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flow is the foundation of nearly every web application you'll work with.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Networking is one of the most important skills in DevOps.&lt;/p&gt;

&lt;p&gt;Understanding IP addresses, DNS, ports, and client-server communication gives you the foundation needed for cloud computing, Docker, Kubernetes, load balancers, CI/CD pipelines, and modern infrastructure.&lt;/p&gt;

&lt;p&gt;The better you understand how systems communicate, the easier it becomes to troubleshoot real-world production issues.&lt;/p&gt;

&lt;p&gt;If you have favorite networking commands or troubleshooting tips, feel free to share them in the comments.&lt;/p&gt;

&lt;p&gt;Happy Learning! 🌐🚀&lt;/p&gt;

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