<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michelle </title>
    <description>The latest articles on Forem by Michelle  (@michellewanjiru).</description>
    <link>https://forem.com/michellewanjiru</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3718292%2F587d92b2-0144-4ec4-8e63-bef9d7f4d0ef.jpg</url>
      <title>Forem: Michelle </title>
      <link>https://forem.com/michellewanjiru</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/michellewanjiru"/>
    <language>en</language>
    <item>
      <title>The Lives Behind the Flowers</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sun, 10 May 2026 01:39:37 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/the-lives-behind-the-flowers-58o3</link>
      <guid>https://forem.com/michellewanjiru/the-lives-behind-the-flowers-58o3</guid>
      <description>&lt;h1&gt;
  
  
  The Quiet Cost of Motherhood
&lt;/h1&gt;

&lt;p&gt;Today being mother's day, I found myself thinking about motherhood, not only in human beings, but across nature itself.&lt;/p&gt;

&lt;p&gt;I came across flamingos, whose bright pink feathers begin to fade while nurturing their young because so much of their energy and nutrients are redirected into feeding their chicks. I read about rabbits pulling out their own fur to prepare warm nests before giving birth. Across species, motherhood seems to come with a quiet surrender. A giving away of the self so that another life may continue.&lt;/p&gt;

&lt;p&gt;And suddenly, human motherhood did not seem so separate from the rest of nature.&lt;/p&gt;

&lt;p&gt;We often speak about birth as though it is the finish line. As though motherhood begins and ends in a delivery room. But giving birth is only the opening chapter.&lt;/p&gt;

&lt;p&gt;Motherhood stretches into sleepless nights, postponed dreams, changing bodies, financial burdens, emotional exhaustion, invisible fear, and the constant responsibility of shaping another human being. Some women lose their hair. Others lose parts of their identity they are still trying to recover years later. Some carry scars no one sees because society has romanticized sacrifice so deeply that pain is expected to arrive silently.&lt;/p&gt;

&lt;p&gt;And still, mothers continue.&lt;/p&gt;

&lt;p&gt;Not because motherhood is always beautiful, but because love often asks people to endure things that beauty alone cannot sustain.&lt;/p&gt;

&lt;p&gt;What unsettles me is how casually society treats this sacrifice while simultaneously condemning women who choose not to become mothers at all.&lt;/p&gt;

&lt;p&gt;There is an assumption that womanhood naturally leads to motherhood, and that rejecting one somehow rejects the other. Women who choose not to have children are often labeled selfish, immature, incomplete, or unnatural. But perhaps what society calls selfishness is sometimes honesty.&lt;/p&gt;

&lt;p&gt;Perhaps some women understand the weight of motherhood deeply enough to admit they are not prepared for it.&lt;/p&gt;

&lt;p&gt;And is that not more responsible than bringing life into the world simply because tradition demanded it?&lt;/p&gt;

&lt;p&gt;Not every person is meant for parenthood. Some know they do not have the emotional, mental, financial, or personal capacity to raise a child in the way a child deserves. Others simply do not desire motherhood at all. Neither should make them villains.&lt;/p&gt;

&lt;p&gt;Nature itself teaches us that nurturing life demands sacrifice. It demands energy, patience, and pieces of oneself that may never fully return. If we truly respected motherhood, then we would also respect the seriousness of choosing whether or not to enter it.&lt;/p&gt;

&lt;p&gt;Because motherhood should never be treated as a compulsory milestone.&lt;/p&gt;

&lt;p&gt;It is a commitment vast enough to alter bodies, identities, futures, and entire lives.&lt;/p&gt;

&lt;p&gt;And perhaps the women who recognize that truth most clearly are not selfish at all.&lt;/p&gt;

&lt;p&gt;Perhaps they are simply honest.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>discuss</category>
      <category>science</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 12 — Terraform: Infrastructure as Code</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Mon, 04 May 2026 08:35:10 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-12-terraform-infrastructure-as-code-53gg</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-12-terraform-infrastructure-as-code-53gg</guid>
      <description>&lt;p&gt;&lt;strong&gt;So far in this challenge, I've built containers, set up CI/CD with Jenkins, and pushed images to Docker Hub.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there was one missing piece: how do I create the actual servers where all this runs?&lt;/p&gt;

&lt;p&gt;Today, I started learning Terraform, a tool that lets you create infrastructure (servers, networks, databases) using code instead of clicking around in a web console.&lt;/p&gt;

&lt;p&gt;No more "ClickOps". No more "it works on my machine". Just code that creates real infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What is Infrastructure as Code (IaC)?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Manual Way (ClickOps)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Console -&amp;gt; Click "Launch Instance" -&amp;gt; Choose settings -&amp;gt; Click "Create"
EC2 Console -&amp;gt; Click "Create Security Group" -&amp;gt; Add rules -&amp;gt; Click "Create"
VPC Console -&amp;gt; Click "Create VPC" -&amp;gt; Configure -&amp;gt; Click "Create"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No record of what you created&lt;/li&gt;
&lt;li&gt;Can't reproduce exactly the same way twice&lt;/li&gt;
&lt;li&gt;Easy to make mistakes&lt;/li&gt;
&lt;li&gt;No version control&lt;/li&gt;
&lt;li&gt;Only the person who clicked knows what happened&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The IaC Way (Terraform)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"my_server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0c55b159cbfafe1f0"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is documentation (everyone can see what exists)&lt;/li&gt;
&lt;li&gt;Version control with Git (track every change)&lt;/li&gt;
&lt;li&gt;Repeatable everywhere (dev, staging, production)&lt;/li&gt;
&lt;li&gt;Easy to review changes before applying&lt;/li&gt;
&lt;li&gt;No human error from clicking wrong buttons&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Terraform?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt; is an open-source tool created by HashiCorp that lets you define infrastructure as code and then creates that infrastructure for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Analogy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terraform code&lt;/td&gt;
&lt;td&gt;A blueprint for a house&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform plan&lt;/td&gt;
&lt;td&gt;Walking through the blueprint before building&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform apply&lt;/td&gt;
&lt;td&gt;Actually building the house&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform destroy&lt;/td&gt;
&lt;td&gt;Demolishing the house&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State file&lt;/td&gt;
&lt;td&gt;The house's deed (records what was built)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Which Providers Does Terraform Support?
&lt;/h3&gt;

&lt;p&gt;Terraform works with almost EVERY cloud provider:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;What it creates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;EC2 servers, VPC networks, S3 storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure&lt;/td&gt;
&lt;td&gt;Virtual machines, databases, storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Cloud&lt;/td&gt;
&lt;td&gt;Compute instances, Kubernetes clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Pods, services, deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;Containers, images, networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Files on your computer (what we'll use today)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Terraform Core Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write Code → init → plan → apply → destroy
   (main.tf)  (setup) (preview) (create) (cleanup)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command 1: &lt;code&gt;terraform init&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Prepares your working directory for Terraform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Downloads required provider plugins (AWS, Azure, Local, etc.)&lt;/li&gt;
&lt;li&gt;Sets up backend for storing state&lt;/li&gt;
&lt;li&gt;Initializes modules&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.8.0...
- Installed hashicorp/local v2.8.0 (signed by HashiCorp)

Terraform has been successfully initialized!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; First time in a folder, or when you add new providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command 2: &lt;code&gt;terraform plan&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Shows what Terraform WILL do without actually doing it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Compares your code against existing infrastructure&lt;/li&gt;
&lt;li&gt;Shows what will be added, changed, or deleted&lt;/li&gt;
&lt;li&gt;Does NOT make any changes (safe to run anytime)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;Terraform&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;perform&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello from Terraform!"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./hello.txt"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Plan&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Always run this BEFORE &lt;code&gt;apply&lt;/code&gt; to review what will change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command 3: &lt;code&gt;terraform apply&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Actually creates or updates infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Shows you the plan&lt;/li&gt;
&lt;li&gt;Asks for confirmation (&lt;code&gt;yes&lt;/code&gt; to proceed)&lt;/li&gt;
&lt;li&gt;Creates the resources&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Do you want to perform these actions?
  Enter a value: yes

local_file.hello: Creating...
local_file.hello: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; After reviewing the plan, to actually create resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command 4: &lt;code&gt;terraform destroy&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Removes all infrastructure created by Terraform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Shows what will be destroyed&lt;/li&gt;
&lt;li&gt;Asks for confirmation&lt;/li&gt;
&lt;li&gt;Deletes everything&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Do you want to destroy these resources? Enter a value: yes

local_file.hello: Destroying...
local_file.hello: Destruction complete

Destroy complete! Resources: 1 destroyed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; When you want to clean up and avoid ongoing charges.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your First Terraform Project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install Terraform
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;On Ubuntu/Kali Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;terraform &lt;span class="nt"&gt;--classic&lt;/span&gt;
terraform &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a Project Folder
&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/Desktop/terraform-demo
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/terraform-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Create main.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Add the Terraform Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello from Terraform!"&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/hello.txt"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding the code:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&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;&lt;code&gt;resource&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"I want to create something"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"local_file"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The type of resource (a file on my computer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"hello"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A name I give this resource (can be anything)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What text to put inside the file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filename&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where to save the file (&lt;code&gt;./hello.txt&lt;/code&gt; means current folder)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${path.module}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terraform variable meaning "current folder"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 5: Initialize Terraform
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Downloads the "local" provider plugin so Terraform can create files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Preview the Plan
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Shows you what Terraform WILL create without actually creating it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;Terraform&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;perform&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello from Terraform!"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./hello.txt"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Plan&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Apply the Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When prompted, type:&lt;/strong&gt; &lt;code&gt;yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Actually creates the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Verify the File Was Created
&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;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt; &lt;code&gt;Hello from Terraform!&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Check Terraform State
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform show
terraform state list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Shows what Terraform is tracking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10: Clean Up (Optional)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Removes the file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding Terraform Files
&lt;/h2&gt;

&lt;p&gt;When you run Terraform, it creates several files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Should you edit it?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your infrastructure code&lt;/td&gt;
&lt;td&gt;YES (this is where you write code)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform.tfstate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current state of infrastructure&lt;/td&gt;
&lt;td&gt;NO (Terraform manages this)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform.tfstate.backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backup of previous state&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.terraform/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provider plugins&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.terraform.lock.hcl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Locks provider versions&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The State File is IMPORTANT
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it contains:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"managed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"instances"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello from Terraform!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./hello.txt"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Terraform uses this file to track what it created. Without it, Terraform doesn't know what exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt; Store state files remotely (S3, Azure Storage) when working with a team.&lt;/p&gt;




&lt;h2&gt;
  
  
  Terraform Commands Cheat Sheet
&lt;/h2&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;What it does&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downloads plugins, sets up backend&lt;/td&gt;
&lt;td&gt;First time in a folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previews what will change&lt;/td&gt;
&lt;td&gt;Before every apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates/updates resources&lt;/td&gt;
&lt;td&gt;After reviewing plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform destroy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes all resources&lt;/td&gt;
&lt;td&gt;When cleaning up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform fmt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Formats code consistently&lt;/td&gt;
&lt;td&gt;Before committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform validate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if code is valid&lt;/td&gt;
&lt;td&gt;After writing code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform show&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shows current state&lt;/td&gt;
&lt;td&gt;To see what exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraform state list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists resources in state&lt;/td&gt;
&lt;td&gt;To see tracked resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Terraform vs Other DevOps Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Where it fits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create infrastructure (servers, networks)&lt;/td&gt;
&lt;td&gt;Day 0: Provision resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ansible&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configure existing servers (install software)&lt;/td&gt;
&lt;td&gt;Day 1: Configure the servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Package applications into containers&lt;/td&gt;
&lt;td&gt;Build phase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jenkins&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automate builds and deployments&lt;/td&gt;
&lt;td&gt;CI/CD pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run and scale containers&lt;/td&gt;
&lt;td&gt;Run phase&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;They work together in this flow:&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;Terraform creates the server
       ↓
Ansible configures the server (installs Docker)
       ↓
Jenkins builds and pushes Docker images
       ↓
Docker runs the containers
       ↓
Kubernetes orchestrates multiple containers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define servers, networks, databases in code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool that turns code into cloud resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets up Terraform in a directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previews what will change (safe to run anytime)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Actually creates/changes resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;destroy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes everything (be careful!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State file&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tracks what Terraform created (don't edit manually)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Now that you understand basic Terraform commands, tomorrow we will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create real infrastructure in the cloud (AWS)&lt;/li&gt;
&lt;li&gt;Provision a server to run your Docker containers&lt;/li&gt;
&lt;li&gt;Combine Terraform with your existing CI/CD pipeline&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From local files to actual cloud servers!&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/docs" rel="noopener noreferrer"&gt;Terraform Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/cli/commands" rel="noopener noreferrer"&gt;Terraform CLI Commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/local/latest/docs" rel="noopener noreferrer"&gt;Local Provider Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform-best-practices.com/" rel="noopener noreferrer"&gt;Terraform Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you used Terraform before? What infrastructure have you coded?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cloud</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 11 — Pushing Docker Images to Registry</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Mon, 04 May 2026 07:34:21 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-11-pushing-docker-images-to-registry-3dml</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-11-pushing-docker-images-to-registry-3dml</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, my Jenkins pipeline could build Docker images for backend and frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But those images only existed on my local machine. They weren't accessible anywhere else, not on another developer's computer, not on a cloud server, not anywhere.&lt;/p&gt;

&lt;p&gt;Today, I fixed that. I configured Jenkins to push Docker images to &lt;strong&gt;Docker Hub&lt;/strong&gt; a public registry where anyone can pull my images.&lt;/p&gt;

&lt;p&gt;Now my images are available everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What is a Docker Registry?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Analogy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker image&lt;/td&gt;
&lt;td&gt;A shipping container with your app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker registry&lt;/td&gt;
&lt;td&gt;A shipping port where containers are stored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;td&gt;The largest public shipping port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docker pull&lt;/td&gt;
&lt;td&gt;Bringing a container FROM the port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docker push&lt;/td&gt;
&lt;td&gt;Sending a container TO the port&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why Push to a Registry?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Registry&lt;/th&gt;
&lt;th&gt;With Registry&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Images only on your laptop&lt;/td&gt;
&lt;td&gt;Images available anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can't share with team&lt;/td&gt;
&lt;td&gt;Anyone can pull the image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need to rebuild on every server&lt;/td&gt;
&lt;td&gt;Pull once, run anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No versioning&lt;/td&gt;
&lt;td&gt;Tag versions (v1.0, latest)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 1: Create Docker Hub Account
&lt;/h2&gt;

&lt;p&gt;If you don't have one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;hub.docker.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign up for a free account&lt;/li&gt;
&lt;li&gt;Remember your username&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 2: Add Docker Hub Credentials to Jenkins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Credentials?
&lt;/h3&gt;

&lt;p&gt;Jenkins needs to log in to Docker Hub before it can push images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Credentials
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open Jenkins: &lt;code&gt;http://localhost:8081&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage Jenkins -&amp;gt; Credentials&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System -&amp;gt; Global credentials -&amp;gt; Add Credentials&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Fill out the form:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kind&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Username with password&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Username&lt;/td&gt;
&lt;td&gt;Your Docker Hub username&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;Your Docker Hub password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-hub-credentials&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Description&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Docker Hub login for pushing images&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;"Create"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 3: Update Jenkinsfile with Push Stage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Complete Jenkinsfile with Push
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DOCKER_REGISTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'docker.io'&lt;/span&gt;
        &lt;span class="n"&gt;DOCKER_USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your-dockerhub-username'&lt;/span&gt;  &lt;span class="c1"&gt;// Replace this!&lt;/span&gt;
        &lt;span class="n"&gt;BACKEND_IMAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${DOCKER_REGISTRY}/${DOCKER_USERNAME}/myapp-backend:latest"&lt;/span&gt;
        &lt;span class="n"&gt;FRONTEND_IMAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${DOCKER_REGISTRY}/${DOCKER_USERNAME}/myapp-frontend:latest"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Cloning repository...'&lt;/span&gt;
                &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="n"&gt;scm&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Backend Dependencies'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Frontend Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'frontend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm run build'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Building Docker images...'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t ${BACKEND_IMAGE} ./backend'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t ${FRONTEND_IMAGE} ./frontend'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Push'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Pushing to Docker Hub...'&lt;/span&gt;
                &lt;span class="n"&gt;withCredentials&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="n"&gt;usernamePassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;credentialsId:&lt;/span&gt; &lt;span class="s1"&gt;'docker-hub-credentials'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;usernameVariable:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_USER'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;passwordVariable:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_PASS'&lt;/span&gt;
                &lt;span class="o"&gt;)])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker push ${BACKEND_IMAGE}'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker push ${FRONTEND_IMAGE}'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Success'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Pipeline completed successfully!'&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Images pushed to:"&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  - ${BACKEND_IMAGE}"&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  - ${FRONTEND_IMAGE}"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Pipeline failed! Check the logs.'&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Images published to Docker Hub!'&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding New Parts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;environment { DOCKER_USERNAME }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores your Docker Hub username&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BACKEND_IMAGE = "docker.io/username/image"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full image name including registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withCredentials()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safely uses credentials without exposing passwords&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authenticates with Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uploads the image to the registry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 4: Replace the Username
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Change &lt;code&gt;your-dockerhub-username&lt;/code&gt; to YOUR actual Docker Hub username!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;DOCKER_USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'johndoe'&lt;/span&gt;  &lt;span class="c1"&gt;// Use your actual username&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Understanding Credential Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What NOT to do (Hardcoding passwords)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DON'T DO THIS!&lt;/span&gt;
&lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker login -u johndoe -p MyRealPassword123'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why not?&lt;/strong&gt; Anyone who sees your Jenkinsfile can steal your password!&lt;/p&gt;

&lt;h3&gt;
  
  
  What TO do (Using Jenkins credentials)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;withCredentials&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="n"&gt;usernamePassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;credentialsId:&lt;/span&gt; &lt;span class="s1"&gt;'docker-hub-credentials'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;usernameVariable:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_USER'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;passwordVariable:&lt;/span&gt; &lt;span class="s1"&gt;'DOCKER_PASS'&lt;/span&gt;
&lt;span class="o"&gt;)])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this is safe:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Password never appears in logs&lt;/li&gt;
&lt;li&gt;Password not stored in Jenkinsfile&lt;/li&gt;
&lt;li&gt;Jenkins encrypts credentials&lt;/li&gt;
&lt;li&gt;Only authorized users can access&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Commit and Push
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add Jenkinsfile
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"ci: add Docker Hub push stage

- Tag images with Docker Hub registry
- Add credentials for Docker login
- Push backend and frontend to Docker Hub"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Run the Pipeline
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to Jenkins -&amp;gt; microservices-ci&lt;/li&gt;
&lt;li&gt;Click "Build Now"&lt;/li&gt;
&lt;li&gt;Watch the console output&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Console Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Docker&lt;/span&gt; &lt;span class="n"&gt;Push&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;
&lt;span class="n"&gt;Pushing&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;Docker&lt;/span&gt; &lt;span class="n"&gt;Hub&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;withCredentials&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;****&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;johndoe&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;
&lt;span class="n"&gt;Login&lt;/span&gt; &lt;span class="n"&gt;Succeeded&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/johndoe/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;backend:&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;refers&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/mkangeth/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nl"&gt;latest:&lt;/span&gt; &lt;span class="nl"&gt;digest:&lt;/span&gt; &lt;span class="nl"&gt;sha256:&lt;/span&gt;&lt;span class="n"&gt;abc123&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;2420&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/johndoe/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;frontend:&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="n"&gt;refers&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/johndoe/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nl"&gt;latest:&lt;/span&gt; &lt;span class="nl"&gt;digest:&lt;/span&gt; &lt;span class="nl"&gt;sha256:&lt;/span&gt;&lt;span class="n"&gt;def456&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// stage&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;
&lt;span class="n"&gt;Pipeline&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt; &lt;span class="n"&gt;successfully&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="n"&gt;Images&lt;/span&gt; &lt;span class="n"&gt;pushed&lt;/span&gt; &lt;span class="nl"&gt;to:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/johndoe/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;backend:&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="s"&gt;/johndoe/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;frontend:&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;span class="nl"&gt;Finished:&lt;/span&gt; &lt;span class="n"&gt;SUCCESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7: Verify on Docker Hub
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;code&gt;https://hub.docker.com/u/your-username&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You should see:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;myapp-backend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;myapp-frontend&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Check Repository Details
&lt;/h3&gt;

&lt;p&gt;Click on &lt;code&gt;myapp-backend&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size:&lt;/strong&gt; ~135MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull command:&lt;/strong&gt; &lt;code&gt;docker pull yourusername/myapp-backend:latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last pushed:&lt;/strong&gt; Just now&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 8: Test Pulling from Any Computer
&lt;/h2&gt;

&lt;p&gt;On any machine with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull your images&lt;/span&gt;
docker pull yourusername/myapp-backend:latest
docker pull yourusername/myapp-frontend:latest

&lt;span class="c"&gt;# Run them&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 yourusername/myapp-backend:latest
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 yourusername/myapp-frontend:latest

&lt;span class="c"&gt;# Test&lt;/span&gt;
curl http://localhost:3001/health
&lt;span class="c"&gt;# Open browser to http://localhost:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It works anywhere in the world!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Before vs After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Before (Day 10)&lt;/th&gt;
&lt;th&gt;After (Day 11)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Images location&lt;/td&gt;
&lt;td&gt;Only on local machine&lt;/td&gt;
&lt;td&gt;Docker Hub (worldwide)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharing&lt;/td&gt;
&lt;td&gt;Send files manually&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker pull&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team access&lt;/td&gt;
&lt;td&gt;Send images via USB/email&lt;/td&gt;
&lt;td&gt;Anyone can pull&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Copy images to server&lt;/td&gt;
&lt;td&gt;Pull from registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version tracking&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Tags in registry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Complete CI/CD Pipeline Now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------------------------------------------------------------+
|                         YOUR COMPLETE CI/CD PIPELINE                              |
+-----------------------------------------------------------------------------------+
|                                                                                   |
|   git push -&amp;gt; Jenkins -&amp;gt; Build -&amp;gt; Docker -&amp;gt; Push -&amp;gt; Available                     |
|                    |          |         |        |                               |
|                    v          v         v        v                               |
|                  Pulls     Installs   Creates  Uploads                           |
|                  code      deps       images   to Hub                            |
|                                                                                   |
+-----------------------------------------------------------------------------------+

                              |
                              v
                    +-----------------+
                    |   DOCKER HUB    |
                    |  (Your Images)  |
                    +-----------------+
                              |
                              v
              +---------------+---------------+
              v               v               v
         Your Laptop     Cloud Server    Team Member
         (docker pull)   (docker pull)   (docker pull)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Images need a home&lt;/strong&gt; = Docker Registry (Docker Hub is the default)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials protect secrets&lt;/strong&gt; = Never hardcode passwords in Jenkinsfile&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag images properly&lt;/strong&gt; = Include registry and username in the tag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push once, pull anywhere&lt;/strong&gt; = Images become globally available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latest is fine, versions are better&lt;/strong&gt; = Consider using build numbers as tags&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/push/" rel="noopener noreferrer"&gt;Docker Push Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/using/using-credentials/" rel="noopener noreferrer"&gt;Jenkins Credentials Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Michelle8395/Production-Ready-Microservices-Platform" rel="noopener noreferrer"&gt;My GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you pushed images to Docker Hub before? What registry do you use (Docker Hub, AWS ECR, GCR, others)?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together!&lt;/p&gt;




</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 10 — Docker in CI</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Mon, 04 May 2026 07:23:51 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-10-docker-in-ci-1d3a</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-10-docker-in-ci-1d3a</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, my Jenkins pipeline could install dependencies and build the frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there was a missing piece: Docker. Without it, I couldn't package my applications into containers — the whole point of this challenge!&lt;/p&gt;

&lt;p&gt;Today, I fixed that. I configured Jenkins to build Docker images for both my backend and frontend, turning my CI pipeline into a complete build system.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: Why Docker in CI?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before Docker in CI
&lt;/h3&gt;

&lt;p&gt;The pipeline could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull code from GitHub&lt;/li&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;li&gt;Build the frontend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Could NOT create Docker images&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  After Docker in CI
&lt;/h3&gt;

&lt;p&gt;The pipeline can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull code from GitHub&lt;/li&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;li&gt;Build the frontend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create Docker images for backend AND frontend&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Why this matters: Docker images are what actually get deployed to production. Without them, you can't run your app anywhere else.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: Giving Jenkins Docker Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Jenkins runs in a container. By default, containers can't access the host's Docker daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------+     +------------------+
|     Jenkins      |     |   Docker Daemon  |
|    Container     |  X  |    (on host)     |
| "I need Docker"  |----&amp;gt;|  "Cannot reach"  |
+------------------+     +------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Solution: Docker Socket Mounting
&lt;/h3&gt;

&lt;p&gt;Mount the host's Docker socket into the Jenkins container:&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="c1"&gt;# jenkins/docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jenkins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;                      &lt;span class="c1"&gt;# Run as root for permissions&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;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;  &lt;span class="c1"&gt;# Mount Docker socket&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOCKER_HOST=unix:///var/run/docker.sock&lt;/span&gt;   &lt;span class="c1"&gt;# Tell Jenkins where Docker is&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After Mounting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------+     +------------------+
|     Jenkins      |     |   Docker Daemon  |
|    Container     |  ✓  |    (on host)     |
| "I need Docker"  |----&amp;gt;|  "Here you go!"  |
+------------------+     +------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Installing Docker in Jenkins Container
&lt;/h2&gt;

&lt;p&gt;Even with the socket mounted, Jenkins needs the Docker CLI tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enter Jenkins container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; jenkins bash

&lt;span class="c"&gt;# Install Docker CLI&lt;/span&gt;
apt-get update
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker.io

&lt;span class="c"&gt;# Verify it works&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Docker version 26.1.5, build a72d7cd
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Adding Docker Build to Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Docker Build Stage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Building Docker images...'&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-backend:latest ./backend'&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-frontend:latest ./frontend'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Command Does
&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a Docker image from a Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t myapp-backend:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tags the image with a name and version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;./backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tells Docker where to find the Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Complete Jenkinsfile (Docker Stage Added)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Cloning repository...'&lt;/span&gt;
                &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="n"&gt;scm&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Backend Dependencies'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Frontend Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'frontend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm run build'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Building Docker images...'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-backend:latest ./backend'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-frontend:latest ./frontend'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Success'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Pipeline completed successfully!'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Understanding Docker Build in CI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Happens During Docker Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;Stage: Docker Build
    │
    ├── Backend Image
    │   ├── FROM node:18-alpine
    │   ├── WORKDIR /app
    │   ├── COPY package*.json ./
    │   ├── RUN npm install
    │   ├── COPY . .
    │   └── CMD ["npm", "start"]
    │
    └── Frontend Image
        ├── FROM nginx:alpine
        ├── COPY build /usr/share/nginx/html
        └── EXPOSE 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Sizes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;myapp-backend:latest&lt;/td&gt;
&lt;td&gt;~135MB&lt;/td&gt;
&lt;td&gt;Node.js + dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;myapp-frontend:latest&lt;/td&gt;
&lt;td&gt;~23MB&lt;/td&gt;
&lt;td&gt;Just nginx + static files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 5: Testing Docker Build Locally
&lt;/h2&gt;

&lt;p&gt;Before trusting Jenkins, test manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build backend image&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; test-backend &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 test-backend
curl http://localhost:3001/health

&lt;span class="c"&gt;# Build frontend image&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../frontend
npm run build
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; test-frontend &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 test-frontend
&lt;span class="c"&gt;# Open browser to http://localhost:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Running the Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trigger the Build
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to Jenkins -&amp;gt; microservices-ci&lt;/li&gt;
&lt;li&gt;Click "Build Now"&lt;/li&gt;
&lt;li&gt;Watch the console output&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Console Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[Pipeline] stage
[Pipeline] { (Docker Build)
[Pipeline] echo
Building Docker images...
[Pipeline] sh
+ docker build -t myapp-backend:latest ./backend
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;1 &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from dockerfile
&lt;span class="gp"&gt;#&lt;/span&gt;1 transferring dockerfile: 149B &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;1 DONE 0.2s
&lt;span class="gp"&gt;#&lt;/span&gt;2 &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/library/node:18-alpine
&lt;span class="gp"&gt;#&lt;/span&gt;2 DONE 35.8s
&lt;span class="gp"&gt;#&lt;/span&gt;3 &lt;span class="o"&gt;[&lt;/span&gt;1/5] FROM docker.io/library/node:18-alpine
&lt;span class="gp"&gt;#&lt;/span&gt;3 DONE 0.0s
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;Successfully built dd22467f3082
Successfully tagged myapp-backend:latest

[Pipeline] sh
+ docker build -t myapp-frontend:latest ./frontend
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;1 &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from dockerfile
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;Successfully built 54515d7d59a9
Successfully tagged myapp-frontend:latest

[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Success)
[Pipeline] echo
Pipeline completed successfully!
Finished: SUCCESS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Docker in CI: Before vs After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Before (Day 9)&lt;/th&gt;
&lt;th&gt;After (Day 10)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker access&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend image&lt;/td&gt;
&lt;td&gt;Not built&lt;/td&gt;
&lt;td&gt;Built automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend image&lt;/td&gt;
&lt;td&gt;Not built&lt;/td&gt;
&lt;td&gt;Built automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ready for deployment&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "docker: command not found"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;+ docker build
docker: command not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install Docker CLI in Jenkins container:&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;docker &lt;span class="nb"&gt;exec &lt;/span&gt;jenkins bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"apt-get update &amp;amp;&amp;amp; apt-get install -y docker.io"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: "Permission denied"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Got permission denied while trying to connect to the Docker daemon
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Run Jenkins as root user in &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: Build context issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;COPY failed: file not found in build context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check your Dockerfile paths. The build context is the directory you specify in &lt;code&gt;docker build -t name ./path&lt;/code&gt;.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker in CI = Deployable artifacts&lt;/strong&gt; — Without Docker, you can't deploy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mount the Docker socket&lt;/strong&gt; — Jenkins container needs access to host's Docker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Docker CLI&lt;/strong&gt; — The container needs the &lt;code&gt;docker&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag images meaningfully&lt;/strong&gt; — &lt;code&gt;latest&lt;/code&gt; is fine, but version tags are better&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build once, deploy anywhere&lt;/strong&gt; — CI builds images, they can run anywhere&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="noopener noreferrer"&gt;Docker Build Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/pipeline/docker/" rel="noopener noreferrer"&gt;Jenkins Docker Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/47709208/how-to-fix-docker-permission-denied-in-jenkins-container" rel="noopener noreferrer"&gt;Mounting Docker Socket&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you integrated Docker with Jenkins before? What challenges did you face with Docker in CI?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together!&lt;/p&gt;




</description>
      <category>automation</category>
      <category>cicd</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 9 — Jenkins Pipeline + GitHub Integration</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Mon, 04 May 2026 07:09:45 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-9-jenkins-pipeline-github-integration-30kk</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-9-jenkins-pipeline-github-integration-30kk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I installed Jenkins and ran my first "Hello World" pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But that was just the beginning. Today, I connected Jenkins to my actual GitHub repository and created a REAL CI pipeline that automatically builds my entire application.&lt;/p&gt;

&lt;p&gt;The result? A fully automated pipeline that pulls code, installs dependencies, builds the frontend, and creates Docker images — all with one click.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What is a CI Pipeline?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CI (Continuous Integration)&lt;/strong&gt; means automatically building and testing your code every time it changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Manual Way (Before)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Every time you change code:&lt;/span&gt;
git pull
&lt;span class="nb"&gt;cd &lt;/span&gt;backend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install
cd&lt;/span&gt; ../frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp-backend ./backend
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp-frontend ./frontend
&lt;span class="c"&gt;# ... repeat forever&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The CI Way (After)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Just push code:&lt;/span&gt;
git push

&lt;span class="c"&gt;# Jenkins does EVERYTHING else automatically!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Connecting Jenkins to GitHub
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating the Pipeline Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Jenkins dashboard -&amp;gt; "New Item"&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;microservices-ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select "Pipeline" -&amp;gt; OK&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configuring GitHub Connection
&lt;/h3&gt;

&lt;p&gt;Scroll down to the "Pipeline" section:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Definition&lt;/td&gt;
&lt;td&gt;Pipeline script from SCM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCM&lt;/td&gt;
&lt;td&gt;Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository URL&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Michelle8395/Production-Ready-Microservices-Platform.git" rel="noopener noreferrer"&gt;https://github.com/Michelle8395/Production-Ready-Microservices-Platform.git&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Branches to build&lt;/td&gt;
&lt;td&gt;*/main&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;What this does: Jenkins will read the pipeline from a Jenkinsfile in your repository.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Creating the Jenkinsfile
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Jenkinsfile&lt;/code&gt; is a text file that defines your entire pipeline as code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Pipeline as Code?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before (UI configuration)&lt;/th&gt;
&lt;th&gt;After (Jenkinsfile)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configuration hidden in Jenkins&lt;/td&gt;
&lt;td&gt;Configuration in your repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard to version control&lt;/td&gt;
&lt;td&gt;Tracked with Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can't reproduce easily&lt;/td&gt;
&lt;td&gt;Anyone can see the pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changes require UI clicks&lt;/td&gt;
&lt;td&gt;Changes just need a commit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Complete Jenkinsfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Cloning repository...'&lt;/span&gt;
                &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="n"&gt;scm&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Backend Dependencies'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Installing backend dependencies...'&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'backend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Frontend Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Building frontend...'&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'frontend'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm install'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'npm run build'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Building Docker images...'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-backend:latest ./backend'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker build -t myapp-frontend:latest ./frontend'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Success'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Pipeline completed successfully!'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding Each Stage
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Checkout&lt;/td&gt;
&lt;td&gt;Pulls code from GitHub&lt;/td&gt;
&lt;td&gt;Always work with latest code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend Dependencies&lt;/td&gt;
&lt;td&gt;Runs npm install&lt;/td&gt;
&lt;td&gt;Ensures all packages are available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend Build&lt;/td&gt;
&lt;td&gt;Installs and builds React&lt;/td&gt;
&lt;td&gt;Creates production-ready static files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Build&lt;/td&gt;
&lt;td&gt;Creates container images&lt;/td&gt;
&lt;td&gt;Packages app for deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Reports completion&lt;/td&gt;
&lt;td&gt;Confirms everything worked&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 3: Running the Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Commit and Push the Jenkinsfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add Jenkinsfile
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"ci: add Jenkins pipeline"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trigger a Build
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Jenkins, go to &lt;code&gt;microservices-ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click "Build Now"&lt;/li&gt;
&lt;li&gt;Watch the console output&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 4: Reading Console Output
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Success Looks Like This:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Pipeline] stage
[Pipeline] { (Backend Dependencies)
[Pipeline] sh
+ npm install
added 94 packages in 2s
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Frontend Build)
[Pipeline] sh
+ npm install
added 1314 packages in 5m
+ npm run build
Compiled successfully.
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Docker Build)
[Pipeline] sh
+ docker build -t myapp-backend:latest ./backend
Successfully built xxxxx
+ docker build -t myapp-frontend:latest ./frontend
Successfully built yyyyy
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Success)
[Pipeline] echo
Pipeline completed successfully!
[Pipeline] }
Finished: SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Color Means
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Color&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;Blue ball&lt;/td&gt;
&lt;td&gt;Build in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Green check&lt;/td&gt;
&lt;td&gt;Build succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Red X&lt;/td&gt;
&lt;td&gt;Build failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yellow dot&lt;/td&gt;
&lt;td&gt;Build unstable (tests failed)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "npm: not found"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;+ npm &lt;span class="nb"&gt;install&lt;/span&gt;
/script.sh: 1: npm: not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Jenkins container doesn't have Node.js installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install Node.js in Jenkins container:&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;docker &lt;span class="nb"&gt;exec &lt;/span&gt;jenkins bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"apt-get update &amp;amp;&amp;amp; apt-get install -y nodejs npm"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: "docker: not found"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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 build
docker: &lt;span class="nb"&gt;command &lt;/span&gt;not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Jenkins can't access Docker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Update &lt;code&gt;jenkins/docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jenkins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&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;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: Git Clone Failed
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;stderr: Permission denied (publickey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Jenkins can't authenticate with GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix for public repos:&lt;/strong&gt; Use HTTPS URL instead of SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/username/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Your Pipeline Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                      JENKINS PIPELINE                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Stage 1: Checkout                                         │
│   └── git clone from GitHub                                 │
│                                                             │
│   Stage 2: Backend Dependencies                             │
│   └── npm install (94 packages)                            │
│                                                             │
│   Stage 3: Frontend Build                                   │
│   ├── npm install (1314 packages)                          │
│   └── npm run build (production)                           │
│                                                             │
│   Stage 4: Docker Build                                     │
│   ├── docker build backend -&amp;gt; myapp-backend:latest          │
│   └── docker build frontend -&amp;gt; myapp-frontend:latest        │
│                                                             │
│   Stage 5: Success                                          │
│   └── Pipeline completed!                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline as Code&lt;/strong&gt; = Your automation lives in Git, not hidden in Jenkins UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stages organize work&lt;/strong&gt; = Each stage has one responsibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console output is your friend&lt;/strong&gt; = Always check logs when something fails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins needs tools&lt;/strong&gt; = Install Node.js, Docker inside the container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI = Continuous Integration&lt;/strong&gt; = Build and test every code change&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/pipeline/syntax/" rel="noopener noreferrer"&gt;Jenkins Pipeline Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/pipeline/jenkinsfile/" rel="noopener noreferrer"&gt;Jenkinsfile Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Michelle8395/Production-Ready-Microservices-Platform" rel="noopener noreferrer"&gt;My GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you set up a CI pipeline before? What challenges did you face?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together!&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devchallenge</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 8 — Jenkins: my First CI/CD Pipeline</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 29 Apr 2026 06:43:36 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-8-jenkins-your-first-cicd-pipeline-311e</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-8-jenkins-your-first-cicd-pipeline-311e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I had all three containers running with Docker Compose;  frontend, backend, and PostgreSQL.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there was still one manual step: every time I changed code, I had to rebuild and restart containers manually.&lt;/p&gt;

&lt;p&gt;Today, I fixed that forever.&lt;/p&gt;

&lt;p&gt;I installed Jenkins — an automation server that watches my GitHub repository and runs tasks automatically when code changes. This is the heart of CI/CD (Continuous Integration/Continuous Deployment).&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What is Jenkins?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In one sentence:&lt;/strong&gt; Jenkins is a tool that automatically runs tasks when your code changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem Jenkins Solves
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Without Jenkins (where I was):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Manually run &lt;code&gt;docker-compose up --build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wait&lt;/li&gt;
&lt;li&gt;Repeat for every change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;With Jenkins:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Jenkins AUTOMATICALLY builds and deploys&lt;/li&gt;
&lt;li&gt;Done!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Analogy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Jenkins&lt;/th&gt;
&lt;th&gt;With Jenkins&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You bake bread from scratch every morning&lt;/td&gt;
&lt;td&gt;Bread machine bakes automatically when you add ingredients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual, repetitive, error-prone&lt;/td&gt;
&lt;td&gt;Automatic, consistent, reliable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 1: Running Jenkins in Docker
&lt;/h2&gt;

&lt;p&gt;Since my entire stack is already containerized, running Jenkins in Docker made perfect sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Jenkins Docker Compose File
&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;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
&lt;span class="nb"&gt;mkdir &lt;/span&gt;jenkins
&lt;span class="nb"&gt;cd &lt;/span&gt;jenkins
nano docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The docker-compose.yml for Jenkins
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jenkins&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;jenkins/jenkins:lts&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;jenkins&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;always&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:8080"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50000:50000"&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;jenkins_home:/var/jenkins_home&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOCKER_HOST=unix:///var/run/docker.sock&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;jenkins_home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Configuration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image: jenkins/jenkins:lts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses the Long-Term Support version&lt;/td&gt;
&lt;td&gt;Stable, reliable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ports: - "8081:8080"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maps host port 8081 → container port 8080&lt;/td&gt;
&lt;td&gt;Access Jenkins at localhost:8081&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;volumes: - jenkins_home:/var/jenkins_home&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Persists Jenkins data&lt;/td&gt;
&lt;td&gt;Jobs, settings survive container restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;volumes: - /var/run/docker.sock:/var/run/docker.sock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mounts Docker socket&lt;/td&gt;
&lt;td&gt;Jenkins can run Docker commands!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DOCKER_HOST=unix:///var/run/docker.sock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tells Jenkins where Docker is&lt;/td&gt;
&lt;td&gt;Allows building images inside Jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Starting Jenkins
&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;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Running 2/2
 ✔ Network jenkins_default     Created
 ✔ Container jenkins           Started
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Accessing Jenkins Web Interface
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Jenkins is Running
&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;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE                 COMMAND   STATUS         PORTS
&lt;/span&gt;&lt;span class="gp"&gt;xxxxxx         jenkins/jenkins:lts   ...       Up ...         0.0.0.0:8081-&amp;gt;&lt;/span&gt;8080/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open Jenkins
&lt;/h3&gt;

&lt;p&gt;In your browser: &lt;code&gt;http://localhost:8081&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Unlocking Jenkins
&lt;/h2&gt;

&lt;p&gt;Jenkins requires an initial admin password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the Password
&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;docker &lt;span class="nb"&gt;exec &lt;/span&gt;jenkins &lt;span class="nb"&gt;cat&lt;/span&gt; /var/jenkins_home/secrets/initialAdminPassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; A long string of letters and numbers (copy this!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Paste the Password
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;On the Jenkins web page, paste the password&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Continue"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 4: Installing Plugins
&lt;/h2&gt;

&lt;p&gt;Jenkins asked which plugins to install. I chose &lt;strong&gt;"Install suggested plugins"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are plugins?&lt;/strong&gt; Extensions that add features to Jenkins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git integration&lt;/li&gt;
&lt;li&gt;Docker pipeline&lt;/li&gt;
&lt;li&gt;Blue Ocean (pretty UI)&lt;/li&gt;
&lt;li&gt;Many more&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Creating Admin User
&lt;/h2&gt;

&lt;p&gt;After plugins installed, I created my admin account:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What I entered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Username&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Password&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(created a strong password)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;My name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Email&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;My email&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Click "Save and Continue"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Jenkins is Ready!
&lt;/h2&gt;

&lt;p&gt;After confirming the URL (default is fine), I clicked &lt;strong&gt;"Start using Jenkins"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I saw the Jenkins dashboard!&lt;/strong&gt; &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: My First Pipeline Job
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating a New Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;"New Item"&lt;/strong&gt; on the left menu&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;my-first-pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;"Pipeline"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Writing the Pipeline Script
&lt;/h3&gt;

&lt;p&gt;In the &lt;strong&gt;"Pipeline"&lt;/strong&gt; section, I selected &lt;strong&gt;"Pipeline script"&lt;/strong&gt; and added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Hello from Jenkins!'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Date'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'date'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Who Am I'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'whoami'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Docker Version'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'docker --version'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What This Pipeline Does
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Output example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hello&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prints a greeting&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Hello from Jenkins!&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs &lt;code&gt;date&lt;/code&gt; command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Tue Apr 28 14:30:00 EAT 2026&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who Am I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shows user Jenkins runs as&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;root&lt;/code&gt; (or &lt;code&gt;jenkins&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Checks Docker is available&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Docker version 28.5.2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Running the Pipeline
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;"Save"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Build Now"&lt;/strong&gt; on the left menu&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;build #1&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Console Output"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&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;[Pipeline] Start of Pipeline
[Pipeline] stage
[Pipeline] { (Hello)
[Pipeline] echo
Hello from Jenkins!
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Date)
[Pipeline] sh
+ date
Tue Apr 29 10:30:00 EAT 2026
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Docker Version)
[Pipeline] sh
+ docker --version
Docker version 28.5.2, build 9cc6dea35e
[Pipeline] }
[Pipeline] End of Pipeline
Finished: SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It worked!&lt;/strong&gt; Jenkins successfully ran my pipeline!&lt;/p&gt;




&lt;h2&gt;
  
  
  Jenkins vs Docker Compose (Understanding the Difference)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker Compose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs multiple containers together&lt;/td&gt;
&lt;td&gt;Starting your app locally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jenkins&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automates tasks when code changes&lt;/td&gt;
&lt;td&gt;CI/CD, automatic builds, testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;They work TOGETHER:&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;You push code → Jenkins sees it → Jenkins runs Docker Compose → Your app updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Issues I Ran Into (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: Docker Pull Timeout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;dial tcp: lookup registry-1.docker.io: i/o timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; DNS configuration issue with Docker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create Docker daemon config&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/docker/daemon.json

&lt;span class="c"&gt;# Add DNS servers&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"dns"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;, &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Restart Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: Port Already in Use
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;Bind for 0.0.0.0:8080 failed: port is already allocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Something else was using port 8080 (maybe another Jenkins or web server).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Used port 8081 instead of 8080.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 3: Jenkins Container Not Starting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check logs:&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;docker logs jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Docker Commands for Jenkins
&lt;/h2&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker-compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start Jenkins in background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker-compose down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop Jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker logs jenkins&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View Jenkins logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get admin password&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Pipeline Syntax Basics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Groovy Element&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pipeline { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wraps the entire pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;agent any&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run on any available agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stages { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Contains all stages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stage('name') { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A named step in the pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;steps { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Contains the actual commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo 'text'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print text to console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sh 'command'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a shell command&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins automates repetitive tasks&lt;/strong&gt; — No more manual builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD = Continuous Integration/Continuous Deployment&lt;/strong&gt; — Automate from code push to deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins runs in Docker too&lt;/strong&gt; — Consistent with our containerized stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins extend Jenkins&lt;/strong&gt; — Git, Docker, Slack, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline as Code&lt;/strong&gt; — Your build process lives in code, not 
click-ops&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/" rel="noopener noreferrer"&gt;Jenkins Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jenkins.io/doc/book/pipeline/syntax/" rel="noopener noreferrer"&gt;Pipeline Syntax Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/jenkins/jenkins" rel="noopener noreferrer"&gt;Docker Jenkins Image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you used Jenkins before? What CI/CD tool do you prefer — Jenkins, GitHub Actions, GitLab CI, or something else?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;

</description>
      <category>automation</category>
      <category>cicd</category>
      <category>devchallenge</category>
      <category>devops</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 7 — Docker Compose: One Command to Rule Them All</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sun, 12 Apr 2026 14:35:05 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-7-docker-compose-one-command-to-rule-them-all-1ll</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-7-docker-compose-one-command-to-rule-them-all-1ll</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I had two containers: one for my backend, one for my frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But running them meant two terminals, two &lt;code&gt;docker run&lt;/code&gt; commands, and remembering which port mapped to what.&lt;/p&gt;

&lt;p&gt;Today, I fixed that forever.&lt;/p&gt;

&lt;p&gt;I learned Docker Compose a tool that lets me define my entire application stack in one file and start everything with a SINGLE command.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Problem Does Docker Compose Solve?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without Docker Compose (Days 5 &amp;amp; 6)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; backend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app

&lt;span class="c"&gt;# Terminal 2 (new terminal)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
npm run build
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; frontend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The pain:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple terminals&lt;/li&gt;
&lt;li&gt;Many commands to remember&lt;/li&gt;
&lt;li&gt;Containers don't automatically know about each other&lt;/li&gt;
&lt;li&gt;Hard to share setup with others&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With Docker Compose (Today)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One terminal. One command.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt; Everything runs.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;In one sentence:&lt;/strong&gt; Docker Compose is a tool that lets you define and run multiple Docker containers together using a single YAML file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Analogy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Docker Alone&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Individual containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shipping containers&lt;/td&gt;
&lt;td&gt;Shipping containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managing them&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moving each by hand&lt;/td&gt;
&lt;td&gt;A crane operator with a manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remember what goes where&lt;/td&gt;
&lt;td&gt;Everything in one file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Docker Compose is the crane operator + manifest that organizes all your containers at once.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;First, I went to my project root:&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;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I created the Compose file:&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;touch &lt;/span&gt;docker-compose.yml
nano docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Writing the Compose File (Line by Line)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Line 1: Version
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means:&lt;/strong&gt; Which version of Docker Compose syntax to use (3.8 is current).&lt;/p&gt;

&lt;h3&gt;
  
  
  Line 2: Services Section
&lt;/h3&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;&lt;strong&gt;What this means:&lt;/strong&gt; Everything below defines containers (called "services").&lt;/p&gt;

&lt;h3&gt;
  
  
  Lines 3-6: Backend Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;backend&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;./backend&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;3001:5000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&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;&lt;code&gt;backend:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build: ./backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build image using Dockerfile in &lt;code&gt;./backend&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ports:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define port mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;- "3001:5000"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map host 3001 → container 5000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lines 7-10: Frontend Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;frontend&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;./frontend&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;Same pattern, but for the frontend (port 8080 → 80).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Complete File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&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;3001:5000"&lt;/span&gt;

  &lt;span class="na"&gt;frontend&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;./frontend&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;&lt;strong&gt;That's it!&lt;/strong&gt; 12 lines to define my entire application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: What Docker Compose Does Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;When I ran &lt;code&gt;docker-compose up --build&lt;/code&gt;, Compose did ALL of this automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Created a shared network&lt;/span&gt;
docker network create microservices-platform_default

&lt;span class="c"&gt;# 2. Built backend image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; microservices-platform-backend ./backend

&lt;span class="c"&gt;# 3. Built frontend image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; microservices-platform-frontend ./frontend

&lt;span class="c"&gt;# 4. Started backend container&lt;/span&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; microservices-platform-backend-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; microservices-platform_default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  microservices-platform-backend

&lt;span class="c"&gt;# 5. Started frontend container&lt;/span&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; microservices-platform-frontend-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; microservices-platform_default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="se"&gt;\&lt;/span&gt;
  microservices-platform-frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All of that from one command.&lt;/strong&gt; That's the magic of Docker Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Running Everything
&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;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 2.5s (15/15) FINISHED
[+] Running 3/3
 ✔ backend   Built
 ✔ frontend  Built
 ✔ Network microservices-platform_default  Created
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the containers started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;frontend-1  | /docker-entrypoint.sh: Configuration complete;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ready &lt;span class="k"&gt;for &lt;/span&gt;start up
&lt;span class="go"&gt;backend-1   | Server is running on http://localhost:5000
backend-1   | Try these endpoints:
backend-1   |   - http://localhost:5000/
backend-1   |   - http://localhost:5000/health
backend-1   |   - http://localhost:5000/users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Both containers running simultaneously in ONE terminal!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing Everything
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing the Backend
&lt;/h3&gt;

&lt;p&gt;In a new 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 http://localhost:3001/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"backend-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-04-12T13:10:49.057Z"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the Frontend
&lt;/h3&gt;

&lt;p&gt;In my browser: &lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My React app appeared!&lt;/strong&gt; Same as Day 3, but now running inside a container managed by Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Stopping Everything
&lt;/h2&gt;

&lt;p&gt;To stop all containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In the Compose terminal, press Ctrl+C&lt;/span&gt;
Ctrl+C

&lt;span class="c"&gt;# Then clean up&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One command stops EVERYTHING.&lt;/strong&gt; No more hunting for container IDs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker vs Docker Compose: The Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Docker Alone&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Run one container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Run multiple containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose up&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connect containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual network setup&lt;/td&gt;
&lt;td&gt;Automatic network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Start order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual (wait, then start)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;depends_on&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment variables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-e&lt;/code&gt; flag each time&lt;/td&gt;
&lt;td&gt;In file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-p&lt;/code&gt; flag each time&lt;/td&gt;
&lt;td&gt;In file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Share setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Send commands to friend&lt;/td&gt;
&lt;td&gt;Send one file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rebuild after changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual rebuild&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--build&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;View logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why Your Project NEEDS Docker Compose
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Your Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → Frontend (React) → Backend (Node.js) → (Soon: PostgreSQL)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Each component has different needs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Frontend&lt;/th&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Database (coming)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Base image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;nginx:alpine&lt;/td&gt;
&lt;td&gt;node:18-alpine&lt;/td&gt;
&lt;td&gt;postgres:15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;td&gt;5432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Persistent volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Start order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Last&lt;/td&gt;
&lt;td&gt;Middle&lt;/td&gt;
&lt;td&gt;First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scale for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User traffic&lt;/td&gt;
&lt;td&gt;API calls&lt;/td&gt;
&lt;td&gt;Data growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Without Compose (Manual Nightmare)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start database&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;-v&lt;/span&gt; postgres_data:/var/lib/postgresql/data postgres:15

&lt;span class="c"&gt;# Wait for database&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;5

&lt;span class="c"&gt;# Start backend&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; backend &lt;span class="nt"&gt;--link&lt;/span&gt; postgres &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres backend-app

&lt;span class="c"&gt;# Start frontend&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; frontend &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app

&lt;span class="c"&gt;# Create network&lt;/span&gt;
docker network create myapp
docker network connect myapp postgres backend frontend

&lt;span class="c"&gt;# To stop: 4 separate commands&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;15+ commands!&lt;/strong&gt; One mistake and nothing works.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Compose (One File)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&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;postgres:15&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;postgres_data:/var/lib/postgresql/data&lt;/span&gt;

  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&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;postgres&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_HOST=postgres&lt;/span&gt;

  &lt;span class="na"&gt;frontend&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;./frontend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3 commands total.&lt;/strong&gt; Perfect every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Issues I Ran Into
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "no configuration file provided"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;no configuration file provided: not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; I was in the wrong directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; docker-compose.yml  &lt;span class="c"&gt;# Verify file exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: Port already allocated
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;Bind for 0.0.0.0:8080 failed: port is already allocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; I still had my old frontend container running from Day 6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps  &lt;span class="c"&gt;# Find running containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop &amp;lt;container_id&amp;gt;  &lt;span class="c"&gt;# Stop the old one&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose up  &lt;span class="c"&gt;# Now it works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: The "version is obsolete" warning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Warning:&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;the attribute `version` is obsolete, it will be ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; I can remove the &lt;code&gt;version: '3.8'&lt;/code&gt; line — newer Docker versions don't need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Compose Commands Cheat Sheet
&lt;/h2&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start all services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start in background (detached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up --build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebuild images then start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and remove containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose down -v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also remove volumes (databases!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show status of services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from all services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from backend only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebuild images without starting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose restart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart all services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose = Multiple containers, one command&lt;/strong&gt; — No more terminal juggling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything in one file&lt;/strong&gt; — Share your setup with anyone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic networking&lt;/strong&gt; — Containers can find each other by name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative configuration&lt;/strong&gt; — Say WHAT you want, not HOW&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local = Production&lt;/strong&gt; — Same Compose file works everywhere&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/compose-file/" rel="noopener noreferrer"&gt;Compose File Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/blog/what-is-docker-compose-vs-kubernetes/" rel="noopener noreferrer"&gt;Docker Compose vs Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 6 — Dockerizing My React Frontend</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sat, 11 Apr 2026 14:55:23 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-6-dockerizing-my-react-frontend-4na7</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-6-dockerizing-my-react-frontend-4na7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I packaged my backend into a Docker container. Today, I did the same for my React frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there's a twist, frontend containers are DIFFERENT. No Node.js running. No &lt;code&gt;npm start&lt;/code&gt;. Just pure, static files served by nginx.&lt;/p&gt;

&lt;p&gt;And I learned why nginx is the king of web servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: How Frontend Docker is Different
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend Container&lt;/th&gt;
&lt;th&gt;Frontend Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runs Node.js server&lt;/td&gt;
&lt;td&gt;Runs nginx (web server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs &lt;code&gt;npm start&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Just serves static files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code changes often&lt;/td&gt;
&lt;td&gt;Build once, serve forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port 5000&lt;/td&gt;
&lt;td&gt;Port 80 (standard web port)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~135MB image&lt;/td&gt;
&lt;td&gt;~23MB image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend = Restaurant kitchen (active cooking, handling requests)&lt;/li&gt;
&lt;li&gt;Frontend = Dining room + menu (just shows what's ready)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Building React for Production
&lt;/h2&gt;

&lt;p&gt;Right now, I run React with &lt;code&gt;npm start&lt;/code&gt; (development mode). For Docker, I need a &lt;strong&gt;production build&lt;/strong&gt;, optimized, minified, and ready to serve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigate to frontend folder
&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;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform/frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the production version
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this command does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes all my React code&lt;/li&gt;
&lt;li&gt;Optimizes it (removes comments, shortens variable names)&lt;/li&gt;
&lt;li&gt;Minifies files (makes them smaller)&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;build&lt;/code&gt; folder with everything needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&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;Creating an optimized production build...
Compiled successfully.

File sizes after gzip:
  40.21 kB  build/static/js/main.xxxxx.js
  1.78 kB   build/static/css/main.xxxxx.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the build folder
&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;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could see the &lt;code&gt;build&lt;/code&gt; folder (blue or highlighted in terminal).&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;build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&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;index.html  static  asset-manifest.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's inside:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.html&lt;/code&gt; — The main HTML file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;static/&lt;/code&gt; — JavaScript and CSS files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asset-manifest.json&lt;/code&gt; — Map of all assets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Creating the Dockerfile
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why nginx?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;nginx (pronounced "engine-x")&lt;/strong&gt; is the most popular web server in the world. It's:&lt;/p&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;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fast&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can handle 10,000+ connections simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alpine version is only 23MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Used by Netflix, Airbnb, Uber, Dropbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Just point it to your files and it works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Create the Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Complete Dockerfile
&lt;/h3&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; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt; Three lines. Frontend containers are much simpler than backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Each Line
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM nginx:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with nginx on Alpine Linux&lt;/td&gt;
&lt;td&gt;Tiny base image (23MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY build /usr/share/nginx/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Put React files where nginx looks&lt;/td&gt;
&lt;td&gt;nginx serves from this folder by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE 80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document the port&lt;/td&gt;
&lt;td&gt;Standard web port&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What is &lt;code&gt;/usr/share/nginx/html&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;This is nginx's &lt;strong&gt;default document root&lt;/strong&gt; — the folder where it looks for files to serve. When you put &lt;code&gt;index.html&lt;/code&gt; there, nginx automatically serves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save and verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Save: Ctrl+O, Enter, Ctrl+X&lt;/span&gt;

&lt;span class="c"&gt;# Verify the file&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&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; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Building the Frontend Image
&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;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; frontend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&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;&lt;code&gt;sudo docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t frontend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag it as "frontend-app"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use Dockerfile in current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 0.5s (5/5) FINISHED
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1/2] FROM nginx:alpine
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;2/2] COPY build /usr/share/nginx/html
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/frontend-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the image
&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;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
frontend-app   latest    xxxxxxx        1 minute ago    23MB
backend-app    latest    e0e0904815be   1 day ago       135MB
nginx          alpine    xxxxxxx        2 weeks ago     23MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notice the size difference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend image: 135MB (Node.js + dependencies)&lt;/li&gt;
&lt;li&gt;Frontend image: 23MB (just static files + nginx)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Running the Frontend Container
&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;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding the port mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&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;&lt;code&gt;-p 8080:80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 8080 (my computer) → port 80 (container)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frontend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which image to use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why port 80?&lt;/strong&gt; Port 80 is the standard web port. Browsers use it by default when you visit &lt;code&gt;http://example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why map to 8080?&lt;/strong&gt; Port 80 is often already in use. 8080 is a common alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt; Nothing in the terminal. That's normal! nginx runs silently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing the Container
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Open browser
&lt;/h3&gt;

&lt;p&gt;I went to: &lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My React app appeared!&lt;/strong&gt; The same one I built on Day 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check running containers
&lt;/h3&gt;

&lt;p&gt;In a &lt;strong&gt;new terminal&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS
&lt;/span&gt;&lt;span class="gp"&gt;b8194975c784   frontend-app   "/docker-entrypoint.…"   3 minutes ago   Up 3 minutes   0.0.0.0:8080-&amp;gt;&lt;/span&gt;80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this shows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container is running&lt;/li&gt;
&lt;li&gt;Port 8080 on my computer → port 80 inside container&lt;/li&gt;
&lt;li&gt;Status: &lt;code&gt;Up&lt;/code&gt; (healthy!)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Running Both Containers
&lt;/h2&gt;

&lt;p&gt;Now I have TWO containers running:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Port&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;frontend-app&lt;/td&gt;
&lt;td&gt;nginx:alpine&lt;/td&gt;
&lt;td&gt;8080&lt;/td&gt;
&lt;td&gt;Serves React app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backend-app&lt;/td&gt;
&lt;td&gt;node:18-alpine&lt;/td&gt;
&lt;td&gt;3001&lt;/td&gt;
&lt;td&gt;API endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;To see both:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE           COMMAND                  PORTS
&lt;/span&gt;&lt;span class="gp"&gt;b8194975c784   frontend-app    "/docker-entrypoint.…"   0.0.0.0:8080-&amp;gt;&lt;/span&gt;80/tcp
&lt;span class="gp"&gt;a1b2c3d4e5f6   backend-app     "docker-entrypoint.s…"   0.0.0.0:3001-&amp;gt;&lt;/span&gt;5000/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Stopping the Container
&lt;/h2&gt;

&lt;p&gt;To stop the frontend container, I went to its terminal and pressed:&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;p&gt;The container stopped gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why nginx for Frontend?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alternative&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;serve&lt;/code&gt; (npm package)&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Needs Node.js installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Not production-ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;nginx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fast, reliable, tiny&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Slight learning curve&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Production Build vs Development
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;npm start&lt;/code&gt; (dev)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;npm run build&lt;/code&gt; (prod)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File size&lt;/td&gt;
&lt;td&gt;Large (unminified)&lt;/td&gt;
&lt;td&gt;Small (minified)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source maps&lt;/td&gt;
&lt;td&gt;Yes (debugging)&lt;/td&gt;
&lt;td&gt;No (smaller)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot reload&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;Performance&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;Production/Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Build Process Explained
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Source Code (JSX, Components)
        ↓
    npm run build
        ↓
Optimized Files (minified, bundled)
        ↓
   build/ folder
        ↓
    COPY to nginx
        ↓
     Served to users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Trying to use &lt;code&gt;npm start&lt;/code&gt; in Docker
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I thought:&lt;/strong&gt; I could just do &lt;code&gt;CMD ["npm", "start"]&lt;/code&gt; like backend&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; React's dev server isn't meant for production containers&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use nginx to serve static files&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Wrong COPY destination
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I tried:&lt;/strong&gt; &lt;code&gt;COPY build /var/www/html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; nginx default folder is &lt;code&gt;/usr/share/nginx/html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use the correct nginx document root&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Forgetting to run &lt;code&gt;npm run build&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; Docker couldn't find the &lt;code&gt;build&lt;/code&gt; folder&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; &lt;code&gt;COPY failed: stat build: file does not exist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always run &lt;code&gt;npm run build&lt;/code&gt; before building the Docker image&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Commands Cheat Sheet (Updated)
&lt;/h2&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create production build of React app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -t name .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build image from Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p HOST:CONTAINER image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run container from image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List 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;List ALL containers (including stopped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi image_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend containers are simpler&lt;/strong&gt; — Just static files + web server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nginx is the industry standard&lt;/strong&gt; — 40% of all websites use it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production builds are essential&lt;/strong&gt; — &lt;code&gt;npm run build&lt;/code&gt; creates optimized files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine images are tiny&lt;/strong&gt; — My frontend image is only 23MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port mapping is still important&lt;/strong&gt; — Container port 80 → host port 8080&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;nginx Official Image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://create-react-app.dev/docs/production-build/" rel="noopener noreferrer"&gt;React Production Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/blog/nginx-vs-apache-vs-node-js/" rel="noopener noreferrer"&gt;nginx vs Apache vs Node.js for serving static files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you containerized a frontend app before? What web server do you prefer? nginx, Apache, or something else?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 6 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&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;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>frontend</category>
      <category>react</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 5 — Dockerizing My Backend (First Steps into Containers)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sat, 11 Apr 2026 14:28:01 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-5-dockerizing-my-backend-first-steps-into-containers-356e</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-5-dockerizing-my-backend-first-steps-into-containers-356e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I had a working PostgreSQL database with real data. But there was one problem...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My backend still ran with &lt;code&gt;npm run dev&lt;/code&gt;. It worked on MY laptop, but would it work anywhere else?&lt;/p&gt;

&lt;p&gt;Today, I solved that forever.&lt;/p&gt;

&lt;p&gt;I packaged my Node.js backend into a Docker container — a self-contained unit that runs IDENTICALLY on any computer. And I learned why containers are revolutionizing software deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Even IS Docker? (Explained Simply)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem Docker Solves
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before Docker (The "It works on my machine" nightmare):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Computer&lt;/th&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your laptop&lt;/td&gt;
&lt;td&gt;Node.js v18, PostgreSQL local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your coworker's laptop&lt;/td&gt;
&lt;td&gt;Node.js v14, different OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud server&lt;/td&gt;
&lt;td&gt;Ubuntu, missing dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Completely different environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; "But it worked on MY machine!" &lt;/p&gt;

&lt;h3&gt;
  
  
  The Docker Solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Think of Docker like a shipping container:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;th&gt;Docker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shipping container&lt;/td&gt;
&lt;td&gt;Docker container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ship, train, truck&lt;/td&gt;
&lt;td&gt;Any computer running Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your stuff inside&lt;/td&gt;
&lt;td&gt;Your app + Node.js + all dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The magic:&lt;/strong&gt; Once it's in a container, it runs IDENTICALLY everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Checking Docker Installation
&lt;/h2&gt;

&lt;p&gt;First, I needed to verify Docker was installed on my Ubuntu system:&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="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&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;Docker version 29.2.1, build a5c7197
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker was installed! But I hit a permission issue:&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;&lt;strong&gt;Error:&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;permission denied while trying to connect to the docker API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fixing Docker Permissions
&lt;/h3&gt;

&lt;p&gt;The issue: My user wasn't in the &lt;code&gt;docker&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add my user to docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker mkangeth

&lt;span class="c"&gt;# Log out and back in (or restart)&lt;/span&gt;
&lt;span class="c"&gt;# Then verify&lt;/span&gt;
docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative (what I used):&lt;/strong&gt; Added &lt;code&gt;sudo&lt;/code&gt; before each docker command:&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;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Empty list = Docker is working! &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Creating the Dockerfile
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Dockerfile&lt;/code&gt; is like a &lt;strong&gt;recipe&lt;/strong&gt; that tells Docker how to build your container.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Dockerfile?
&lt;/h3&gt;

&lt;p&gt;Think of it like baking a cake:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Baking Analogy&lt;/th&gt;
&lt;th&gt;Dockerfile Instruction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Start with base ingredients&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FROM node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set up your workspace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add your ingredients&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COPY package*.json ./&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mix everything&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RUN npm install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Present the final cake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CMD ["npm", "start"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Creating the File
&lt;/h3&gt;

&lt;p&gt;First, navigate to my backend folder:&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;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform/backend
&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 shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Complete Dockerfile (Line by Line)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use Node.js 18 on Alpine Linux (tiny Linux, only 5MB!)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Create and set working directory inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package files first (for Docker caching)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install all dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy all source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Tell Docker the container listens on port 5000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="c"&gt;# Start the application&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Each Line Matters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why it's important&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base image with Node.js&lt;/td&gt;
&lt;td&gt;Alpine is tiny (5MB vs 100MB+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets working directory&lt;/td&gt;
&lt;td&gt;All commands run from here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY package*.json ./&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies dependency list&lt;/td&gt;
&lt;td&gt;Docker caches this layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installs dependencies&lt;/td&gt;
&lt;td&gt;Happens inside the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY . .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies your code&lt;/td&gt;
&lt;td&gt;Last layer (changes most often)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE 5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Documents the port&lt;/td&gt;
&lt;td&gt;Doesn't publish, just informs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMD ["npm", "start"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs when container starts&lt;/td&gt;
&lt;td&gt;Production command, not dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Building the Docker Image
&lt;/h2&gt;

&lt;p&gt;Now the magic happens — building my image:&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;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; backend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&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;&lt;code&gt;sudo docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image from a Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t backend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag (name) the image "backend-app"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use Dockerfile in current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 45.2s (10/10) FINISHED
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1/5] FROM node:18-alpine
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;2/5] WORKDIR /app
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;3/5] COPY package&lt;span class="k"&gt;*&lt;/span&gt;.json ./
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;4/5] RUN npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;5/5] COPY &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/backend-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verifying the Image
&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;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
backend-app    latest    e0e0904815be   2 minutes ago   135MB
node           18-alpine f0286cc18189   2 weeks ago     120MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My image is 135MB — much smaller than a full Ubuntu image would be (thanks, Alpine!)&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Running the Container
&lt;/h2&gt;

&lt;p&gt;Time to run my backend inside 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;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding port mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&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;&lt;code&gt;-p 3001:5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 3001 (my computer) → port 5000 (container)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;backend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which image to use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why port mapping?&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside container: app runs on port 5000&lt;/li&gt;
&lt;li&gt;On my computer: I can access it via port 3001&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;backend@1.0.0 start
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node server.js
&lt;span class="go"&gt;
 Server is running on http://localhost:5000
 Try these endpoints:
   - http://localhost:5000/
   - http://localhost:5000/health
   - http://localhost:5000/users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The terminal stays "stuck" here&lt;/strong&gt; — that's GOOD! My server is running!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing the Container
&lt;/h2&gt;

&lt;p&gt;In a &lt;strong&gt;new terminal&lt;/strong&gt;, I tested my 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;curl http://localhost:3001/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"backend-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-04-11T14:03:42.057Z"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tested the users endpoint:&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:3001/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Charlie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"charlie@example.com"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SUCCESS!&lt;/strong&gt; My Dockerized backend was working perfectly!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Stopping the Container
&lt;/h2&gt;

&lt;p&gt;To stop the container, I went back to the first terminal and pressed:&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;p&gt;The server shut down gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Concepts I Mastered
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What I learned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A blueprint/template (like a class in programming)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A running instance of an image (like an object)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The recipe that defines how to build an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Connecting container ports to host ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layer caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker reuses unchanged layers for faster builds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Docker Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile → docker build → Image → docker run → Container
   (recipe)      (build)     (blueprint)   (run)    (running app)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Alpine Linux?
&lt;/h3&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;Size&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1GB&lt;/td&gt;
&lt;td&gt;Full OS, many tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:18-slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~200MB&lt;/td&gt;
&lt;td&gt;Minimal Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~120MB&lt;/td&gt;
&lt;td&gt;Tiny, security-focused&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;I chose Alpine:&lt;/strong&gt; Smaller = faster downloads, less storage, smaller attack surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Docker Permission Denied
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&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;permission denied while trying to connect to the docker API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added &lt;code&gt;sudo&lt;/code&gt; before docker commands OR added user to docker group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;span class="c"&gt;# Then log out and back in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mistake 2: Wrong Port Mapping
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; My server ran on port 5000, but I mapped to port 8000&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wrong&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:8000 backend-app

&lt;span class="c"&gt;# Correct&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Always check what port your app actually uses!&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Commands Cheat Sheet
&lt;/h2&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check Docker version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images on your computer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List 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;List ALL containers (including stopped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -t name .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image from Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p HOST:CONTAINER image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a container from an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi image_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;See container logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Containers are NOT virtual machines&lt;/strong&gt; — They share the host OS kernel, making them lightweight and fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dockerfiles are recipes&lt;/strong&gt; — They define exactly how to build your container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images are blueprints&lt;/strong&gt; — You build once, run anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers are running instances&lt;/strong&gt; — You can run many containers from one image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port mapping is essential&lt;/strong&gt; — Containers have their own network; you must expose ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine Linux is your friend&lt;/strong&gt; — Tiny images = faster everything&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;Dockerfile Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md" rel="noopener noreferrer"&gt;Node.js Docker Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux (tiny Docker images)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you used Docker before? What was your first container? Any tips for someone just starting?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 5 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&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;

</description>
      <category>devchallenge</category>
      <category>devops</category>
      <category>docker</category>
      <category>node</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 4 — PostgreSQL Deep Dive (From Zero to Database Hero)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 08 Apr 2026 09:13:28 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-4-postgresql-deep-dive-from-zero-to-database-hero-23e9</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-4-postgresql-deep-dive-from-zero-to-database-hero-23e9</guid>
      <description>&lt;p&gt;Yesterday, my React frontend finally talked to my Node.js backend. But there was one problem...&lt;/p&gt;

&lt;p&gt;The users were &lt;strong&gt;FAKE&lt;/strong&gt;. Hardcoded in &lt;code&gt;server.js&lt;/code&gt;. Every time the server restarted, any new users would disappear forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Today, I fixed that permanently.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I installed PostgreSQL, created a database, built a users table, and stored real data that survives server restarts. And I learned WAY more about databases than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Even IS a Database?
&lt;/h2&gt;

&lt;p&gt;Before writing a single command, I needed to understand what a database actually IS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Think of it like this:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real World&lt;/th&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A filing cabinet&lt;/td&gt;
&lt;td&gt;The database itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A drawer in the cabinet&lt;/td&gt;
&lt;td&gt;A table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A folder in the drawer&lt;/td&gt;
&lt;td&gt;A row (record)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Information on the folder&lt;/td&gt;
&lt;td&gt;Columns (fields)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  For my users:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One drawer called &lt;code&gt;users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Each folder has: an ID number, a name, an email address, a creation date&lt;/li&gt;
&lt;li&gt;I can open any folder, read it, change it, or throw it away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The magic:&lt;/strong&gt; Even if I turn off my computer, the filing cabinet (database) still has all my folders.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Installing PostgreSQL
&lt;/h2&gt;

&lt;p&gt;I'm on Ubuntu Linux, so installation was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update my package list (like refreshing the app store)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install PostgreSQL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql postgresql-contrib &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Verify it's running&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What I learned about each command:
&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;What it actually does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Super user do" — gives admin permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apt update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refreshes the list of available software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apt install postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downloads and installs the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatically answers "yes" to confirmation prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The moment I knew it worked:&lt;/strong&gt; Seeing &lt;code&gt;Active: active (running)&lt;/code&gt; in green text.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Understanding PostgreSQL Users
&lt;/h2&gt;

&lt;p&gt;When PostgreSQL installs, it automatically creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A system user called &lt;code&gt;postgres&lt;/code&gt; (the admin/manager)&lt;/li&gt;
&lt;li&gt;A default database also called &lt;code&gt;postgres&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Analogy:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;A bank vault&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;postgres&lt;/code&gt; user&lt;/td&gt;
&lt;td&gt;The bank manager (has all the keys)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;My job&lt;/td&gt;
&lt;td&gt;Ask the manager to create a vault for MY project&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  To become the manager:
&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; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My prompt changed from:&lt;/strong&gt; &lt;code&gt;mkangeth@LAP-018:~$&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;To:&lt;/strong&gt; &lt;code&gt;postgres=#&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; That &lt;code&gt;postgres=#&lt;/code&gt; means I'm INSIDE PostgreSQL now. Different commands work here.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 3: My First PostgreSQL Commands
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Seeing what databases already exist:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This showed me three databases: &lt;code&gt;postgres&lt;/code&gt;, &lt;code&gt;template0&lt;/code&gt;, &lt;code&gt;template1&lt;/code&gt;. None were mine.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating MY database:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE DATABASE&lt;/code&gt; — it worked!&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a user for my app:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'mysecretpassword'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE ROLE&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Giving my user permission:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;GRANT&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Connecting to MY database:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;You are now connected to database "microservices_platform"&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; My prompt changed to &lt;code&gt;microservices_platform=#&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 4: Creating My First Table
&lt;/h2&gt;

&lt;p&gt;Now for the exciting part — creating a table to store users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Let me explain EVERY piece:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make a new table called "users"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id SERIAL PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-number each user (1,2,3...) and use it as the unique identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name VARCHAR(100) NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name column, max 100 characters, can't be empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email VARCHAR(100) UNIQUE NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email column, max 100 chars, no duplicates, can't be empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatically sets the current time when a user is added&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE TABLE&lt;/code&gt; — my table exists!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Inserting Data
&lt;/h2&gt;

&lt;p&gt;Time to add some users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&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="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'alice@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bob@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Charlie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'charlie@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;INSERT 0 3&lt;/code&gt; — three users added successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Reading My Data
&lt;/h2&gt;

&lt;p&gt;The moment of truth — seeing what's actually in my database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;name&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="n"&gt;email&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="c1"&gt;----+---------+---------------------+----------------------------&lt;/span&gt;
 &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Alice&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
 &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Bob&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
 &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Charlie&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;charlie&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I literally cheered.&lt;/strong&gt; My data was stored permanently in a real database!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Exiting PostgreSQL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to my normal terminal prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned About Databases (Deep Dive)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why PostgreSQL Specifically?
&lt;/h3&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;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACID compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transactions are atomic — either fully complete or fully fail (no partial updates)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MVCC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple users can read/write simultaneously without locking each other&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced data types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JSON, arrays, hstore, geometric data — not just strings and numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standards compliant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Learning PostgreSQL teaches you SQL that works on Oracle, DB2, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Data Types I Used
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it stores&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;&lt;code&gt;SERIAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-incrementing integer&lt;/td&gt;
&lt;td&gt;1, 2, 3, 4...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VARCHAR(100)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text up to 100 characters&lt;/td&gt;
&lt;td&gt;"Alice"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date and time&lt;/td&gt;
&lt;td&gt;"2026-04-08 09:57:56"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Constraints I Used
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uniquely identifies each row (no duplicates, never null)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This field must have a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No two rows can have the same value in this column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEFAULT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If no value is provided, use this automatic value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Typing PostgreSQL commands in the normal terminal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;\c&lt;/code&gt; at &lt;code&gt;mkangeth@LAP-018:~$&lt;/code&gt; and got &lt;code&gt;command not found&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;\c&lt;/code&gt; is a PostgreSQL command, not a Linux command&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Enter PostgreSQL first with &lt;code&gt;sudo -u postgres psql&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Forgetting the semicolon
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;CREATE DATABASE microservices_platform&lt;/code&gt; and nothing happened&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; PostgreSQL waits for &lt;code&gt;;&lt;/code&gt; to know the command is complete&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always end SQL commands with &lt;code&gt;;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Incomplete CREATE DATABASE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;CREATE DATABASE microservices&lt;/code&gt; and got stuck at a &lt;code&gt;postgres-#&lt;/code&gt; prompt&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; I didn't finish the command&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Press &lt;code&gt;Ctrl+C&lt;/code&gt; to cancel, then retype the full command&lt;/p&gt;




&lt;h2&gt;
  
  
  PostgreSQL vs Other Databases (What I Learned)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex apps, data integrity&lt;/td&gt;
&lt;td&gt;Full SQL support, ACID compliant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MySQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple web apps, WordPress&lt;/td&gt;
&lt;td&gt;Fast reads, easier to learn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mobile apps, local storage&lt;/td&gt;
&lt;td&gt;No server needed, single file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flexible schema, JSON data&lt;/td&gt;
&lt;td&gt;No fixed structure, horizontal scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firebase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time apps, prototyping&lt;/td&gt;
&lt;td&gt;Built-in auth, real-time updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supabase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL + convenience&lt;/td&gt;
&lt;td&gt;Open source, real-time, auto-API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why I chose PostgreSQL for this project:&lt;/strong&gt; It's the industry standard for serious applications. Apple, Netflix, and Uber all use it.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Databases are permanent storage&lt;/strong&gt; — unlike server memory, data survives restarts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tables have structure&lt;/strong&gt; — columns define what data looks like (types, constraints)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL is powerful&lt;/strong&gt; — ACID compliance, advanced data types, concurrency handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL is the language&lt;/strong&gt; — &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The prompt tells you where you are&lt;/strong&gt; — &lt;code&gt;$&lt;/code&gt; = terminal, &lt;code&gt;=#&lt;/code&gt; = PostgreSQL&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Quick Reference: PostgreSQL Commands I Learned
&lt;/h2&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;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -u postgres psql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enter PostgreSQL as admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\c database_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect to a specific database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all tables in current database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d table_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show table structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE DATABASE name;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE USER name WITH PASSWORD 'pass';&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON DATABASE db TO user;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give user permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO ... VALUES ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add data to a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read data from a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/" rel="noopener noreferrer"&gt;PostgreSQL Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3schools.com/sql/" rel="noopener noreferrer"&gt;SQL Tutorial for Beginners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/about/advantages/" rel="noopener noreferrer"&gt;PostgreSQL vs MySQL Comparison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;







&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you worked with databases before? SQL or NoSQL? What's your preference and why?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




</description>
      <category>beginners</category>
      <category>database</category>
      <category>devchallenge</category>
      <category>postgres</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 3 — Connecting Frontend to Backend (The CORS Journey)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Fri, 03 Apr 2026 10:42:26 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-3-connecting-frontend-to-backend-the-cors-journey-2be9</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-3-connecting-frontend-to-backend-the-cors-journey-2be9</guid>
      <description>&lt;p&gt;After yesterday's backend success, today was about making my React frontend actually &lt;strong&gt;TALK&lt;/strong&gt; to my Node.js API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spoiler alert:&lt;/strong&gt; It wasn't as simple as I thought. But that's where the real learning happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Set Out to Do Today
&lt;/h2&gt;

&lt;p&gt;Build a React frontend that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Connects to my backend API&lt;/li&gt;
&lt;li&gt;✅ Fetches health status automatically&lt;/li&gt;
&lt;li&gt;✅ Displays users from the database&lt;/li&gt;
&lt;li&gt;✅ Shows everything in a nice UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simple, right?&lt;/strong&gt; Well... let me share the journey.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating the React App
&lt;/h2&gt;

&lt;p&gt;I started by creating a React app in my project folder:&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;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
npx create-react-app frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This created a complete React application with all the boilerplate code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;create-react-app&lt;/code&gt; is a magical command that sets up an entire React development environment in minutes. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development server with hot reloading&lt;/li&gt;
&lt;li&gt;Build tools (webpack, Babel)&lt;/li&gt;
&lt;li&gt;Testing setup&lt;/li&gt;
&lt;li&gt;Production build optimization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Cleaning Up Unnecessary Files
&lt;/h2&gt;

&lt;p&gt;React creates many example files. I removed what I didn't need:&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;cd &lt;/span&gt;frontend/src
&lt;span class="nb"&gt;rm &lt;/span&gt;App.test.js setupTests.js reportWebVitals.js logo.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;reportWebVitals.js&lt;/code&gt; tracks performance metrics (load time, responsiveness). Not needed for learning, but useful for production apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oops moment:&lt;/strong&gt; After deleting files, React crashed because &lt;code&gt;index.js&lt;/code&gt; was still trying to import &lt;code&gt;reportWebVitals&lt;/code&gt;. Fixed by removing those import lines from &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Writing the React Component
&lt;/h2&gt;

&lt;p&gt;Here's the complete &lt;code&gt;App.js&lt;/code&gt; I built:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// State = memory for our component&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;backendStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setBackendStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checking backend...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// useEffect = runs automatically when page loads&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;checkBackendHealth&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;span class="c1"&gt;// Check if backend is alive&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkBackendHealth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setBackendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✅ Backend is healthy! Service: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setBackendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Cannot reach backend. Make sure it&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s running on port 8000&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="c1"&gt;// Load users from backend&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load users:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Microservices&lt;/span&gt; &lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Backend&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;backendStatus&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt; &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadUsers&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Load Users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;strong&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/strong&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
               &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;))}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Major Problem: CORS
&lt;/h2&gt;

&lt;p&gt;I ran both servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend on port 8000 (&lt;code&gt;npm run dev&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Frontend on port 3000 (&lt;code&gt;npm start&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But my React app showed:&lt;/strong&gt; ❌ Cannot reach backend&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The browser console showed this error:&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;Access to fetch at 'http://localhost:8000/health' from origin 'http://localhost:3000' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What is CORS? (Simple Explanation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&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;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cross-Origin Resource Sharing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The domain + port where your app lives (&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browsers block requests from one origin to another by default (security)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tell the backend "It's OK to accept requests from React"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React = A person at door 3000&lt;/li&gt;
&lt;li&gt;Backend = A person at door 8000&lt;/li&gt;
&lt;li&gt;Browser = Security guard&lt;/li&gt;
&lt;li&gt;Without CORS = Guard says "You can't talk to each other, different doors!"&lt;/li&gt;
&lt;li&gt;With CORS = Guard says "Oh, it's allowed. Go ahead!"&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Fixing CORS (Step by Step)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install the CORS package
&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;cd &lt;/span&gt;backend
npm &lt;span class="nb"&gt;install &lt;/span&gt;cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add CORS to server.js
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← ADD THIS&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// ← ADD THIS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Restart both servers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Backend: &lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Frontend: &lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm start&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Hard refresh browser
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ctrl + Shift + R&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment It Worked
&lt;/h2&gt;

&lt;p&gt;After fixing CORS, my browser showed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend Status:&lt;/strong&gt; ✅ Backend is healthy! Service: backend-api&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clicking "Load Users" displayed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alice (&lt;a href="mailto:alice@example.com"&gt;alice@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Bob (&lt;a href="mailto:bob@example.com"&gt;bob@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Charlie (&lt;a href="mailto:charlie@example.com"&gt;charlie@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SUCCESS!&lt;/strong&gt; My frontend and backend were finally talking!&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings from Day 3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What I Learned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useState&lt;/code&gt; creates memory for your component. When state changes, UI updates automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;useEffect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs code automatically when component loads (great for fetching initial data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fetch API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser's built-in way to make HTTP requests to APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser security that blocks cross-origin requests unless backend allows it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React uses 3000, backend uses 8000 — different ports = different origins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Problem-Solving Skills
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reading error messages:&lt;/strong&gt; The CORS error looked scary, but it told me exactly what was wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using browser Console:&lt;/strong&gt; &lt;code&gt;F12&lt;/code&gt; → Console tab shows all errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing with curl:&lt;/strong&gt; &lt;code&gt;curl http://localhost:8000/health&lt;/code&gt; proved backend was working (curl ignores CORS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systematic debugging:&lt;/strong&gt; Backend works? Yes. Frontend works? Yes. Connection? CORS issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deleted files without updating imports&lt;/td&gt;
&lt;td&gt;React compilation error&lt;/td&gt;
&lt;td&gt;Removed import lines from index.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Didn't install CORS package&lt;/td&gt;
&lt;td&gt;CORS still blocked&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install cors&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgot to restart backend&lt;/td&gt;
&lt;td&gt;Changes didn't take effect&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Didn't hard refresh browser&lt;/td&gt;
&lt;td&gt;Saw old cached page&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl+Shift+R&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Tips for Anyone Building Their First React + API App
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test backend with curl first&lt;/strong&gt; — If curl works but React doesn't, it's CORS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always restart servers after changes&lt;/strong&gt; — They don't auto-reload config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use browser Console&lt;/strong&gt; — It's your best friend for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard refresh (Ctrl+Shift+R)&lt;/strong&gt; — Avoid seeing cached old versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep both terminals visible&lt;/strong&gt; — One for backend, one for frontend&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react.dev" rel="noopener noreferrer"&gt;React Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORS Explained (MDN)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com/en/resources/middleware/cors.html" rel="noopener noreferrer"&gt;Express CORS Middleware&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you struggled with CORS before? Building your first full-stack app? I'd love to hear about your journey!&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 3 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&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;

</description>
      <category>devchallenge</category>
      <category>node</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 2 — Building My First Backend API</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 01 Apr 2026 13:10:35 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-2-building-my-first-backend-api-2ed5</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-2-building-my-first-backend-api-2ed5</guid>
      <description>&lt;p&gt;&lt;strong&gt;The journey continues!&lt;/strong&gt; After setting up my project structure on Day 1, today was all about building the heart of my microservices platform: the backend API.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you missed Day 1, you can catch up here: [&lt;a href="https://dev.to/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a?trk=public_post_comment-text"&gt;https://dev.to/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a?trk=public_post_comment-text&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Set Out to Do Today
&lt;/h2&gt;

&lt;p&gt;Build a working REST API that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to HTTP requests&lt;/li&gt;
&lt;li&gt;Return JSON data&lt;/li&gt;
&lt;li&gt;Serve as the foundation for my microservices platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simple, right?&lt;/strong&gt; Well... let me share how it actually went.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack I Chose
&lt;/h2&gt;

&lt;p&gt;I decided to go with &lt;strong&gt;Node.js + Express&lt;/strong&gt; for my backend because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript is everywhere (frontend, backend, even DevOps tools)&lt;/li&gt;
&lt;li&gt;Express is lightweight and beginner-friendly&lt;/li&gt;
&lt;li&gt;I already had Node.js installed on my Ubuntu system&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step-by-Step: What I Actually Did
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Initial Setup
&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;cd &lt;/span&gt;backend
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;  &lt;span class="c"&gt;# Creates package.json&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;express  &lt;span class="c"&gt;# Web framework&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; nodemon  &lt;span class="c"&gt;# Auto-restarts server on changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;package.json&lt;/code&gt; is like a shopping list for your project. Every tool you add gets listed there, making it easy to share your project with others.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Writing My First Server
&lt;/h3&gt;

&lt;p&gt;I created &lt;code&gt;server.js&lt;/code&gt; with three endpoints:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Root endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to the Microservices Platform API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Health check endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;healthy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backend-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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;span class="c1"&gt;// Users endpoint (mock data for now)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Charlie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;charlie@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;` Server running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;&lt;strong&gt;Understanding each part:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app.get()&lt;/code&gt; defines what happens when someone visits a URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(req, res) =&amp;gt; {}&lt;/code&gt; is a function that handles the request and sends a response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;res.json()&lt;/code&gt; sends data back as JSON (the language of APIs)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. The Challenge That Almost Stopped Me
&lt;/h3&gt;

&lt;p&gt;I tried running my server with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And got this error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm error Missing script: "dev"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My reaction:&lt;/strong&gt; 😅 &lt;em&gt;Wait, what? I thought I had this!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I diagnosed it:&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;npm run  &lt;span class="c"&gt;# Shows all available scripts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;code&gt;test&lt;/code&gt; was showing. I had completely forgotten to add the &lt;code&gt;"dev"&lt;/code&gt; script to my package.json!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; I opened &lt;code&gt;package.json&lt;/code&gt; and added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon server.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Never assume your configuration is correct. Always verify with simple commands.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. The Moment It Finally Worked
&lt;/h3&gt;

&lt;p&gt;After fixing the script, I ran:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; Server running on http://localhost:8000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in another 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 http://localhost:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Welcome to the Microservices Platform API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"running"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I literally cheered in my chair.🎉&lt;/strong&gt; My first API was alive!&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing All Endpoints
&lt;/h2&gt;

&lt;p&gt;Here's what each endpoint returned:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Welcome message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /health&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service status with timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List of 3 mock users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All working perfectly!&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings from Day 2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Concepts I Gained:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Methods:&lt;/strong&gt; &lt;code&gt;GET&lt;/code&gt; is how you &lt;em&gt;retrieve&lt;/em&gt; data from a server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON:&lt;/strong&gt; The universal language of APIs — both request and response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ports:&lt;/strong&gt; Think of them like doors — 8000 is the door my API listens on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints:&lt;/strong&gt; Specific URLs that do specific things (&lt;code&gt;/health&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package.json scripts:&lt;/strong&gt; Custom commands to automate repetitive tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Problem-Solving Skills:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diagnosing errors:&lt;/strong&gt; &lt;code&gt;npm run&lt;/code&gt; helped me see what was actually available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading error messages:&lt;/strong&gt; "Missing script" told me exactly what was wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step-by-step verification:&lt;/strong&gt; Test each piece before assuming everything works&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mindset Shifts:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Done is better than perfect:&lt;/strong&gt; My first API doesn't have a database yet, and that's okay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Celebrate small wins:&lt;/strong&gt; Getting that first curl response was genuinely exciting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embrace mistakes:&lt;/strong&gt; The missing script error taught me more than if everything worked perfectly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tips for Anyone Starting Their Own API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start simple.&lt;/strong&gt; My API only does 3 things right now — that's enough!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test as you go.&lt;/strong&gt; Don't write 100 lines of code before testing. Test after each endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use curl or browser.&lt;/strong&gt; It's satisfying to see your API respond in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save your package.json changes.&lt;/strong&gt; I almost forgot this one!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep notes.&lt;/strong&gt; Document what you learn, especially the mistakes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Quick Thank You
&lt;/h2&gt;

&lt;p&gt;To everyone following along and the community helping me debug, your support means everything. Special shoutout to those who reminded me to check my package.json scripts!&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next (Day 3)
&lt;/h2&gt;

&lt;p&gt;Tomorrow, I'm building a frontend that will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data from my API&lt;/li&gt;
&lt;li&gt;Display the list of users&lt;/li&gt;
&lt;li&gt;Show the health status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It's going to be the first time my frontend and backend talk to each other!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Links &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Michelle8395/Production-Ready-Microservices-Platform" rel="noopener noreferrer"&gt;Production-Ready-Microservices-Platform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express Documentation:&lt;/strong&gt; &lt;a href="https://expressjs.com" rel="noopener noreferrer"&gt;expressjs.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nodemon:&lt;/strong&gt; &lt;a href="https://nodemon.io" rel="noopener noreferrer"&gt;nodemon.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Are you also learning backend development? Building your first API? I'd love to hear about your journey!&lt;/p&gt;

&lt;p&gt;Drop a comment below or connect with me on LinkedIn at &lt;a href="https://www.linkedin.com/in/michelle-wanjiru-420877275/?lipi=urn%3Ali%3Apage%3Ad_flagship3_detail_base%3BHw%2F74ryNQ7CjXqDkL3k7eQ%3D%3D" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/michelle-wanjiru-420877275/?lipi=urn%3Ali%3Apage%3Ad_flagship3_detail_base%3BHw%2F74ryNQ7CjXqDkL3k7eQ%3D%3D&lt;/a&gt;. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 2 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>api</category>
      <category>backend</category>
      <category>devjournal</category>
      <category>node</category>
    </item>
  </channel>
</rss>
