<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Uchechukwu Enyi </title>
    <description>The latest articles on DEV Community by Uchechukwu Enyi  (@uchemira).</description>
    <link>https://dev.to/uchemira</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%2F3038317%2Fc16ac3be-8df5-4170-b5cc-f701cb1d2b79.png</url>
      <title>DEV Community: Uchechukwu Enyi </title>
      <link>https://dev.to/uchemira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uchemira"/>
    <language>en</language>
    <item>
      <title>Anomaly Detector</title>
      <dc:creator>Uchechukwu Enyi </dc:creator>
      <pubDate>Mon, 27 Apr 2026 14:37:58 +0000</pubDate>
      <link>https://dev.to/uchemira/anomaly-detector-5601</link>
      <guid>https://dev.to/uchemira/anomaly-detector-5601</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Tool That Catches Hackers in Real Time — Here's How It Works
&lt;/h1&gt;




&lt;p&gt;Have you ever wondered how websites protect themselves from being flooded with fake traffic? Or how a server "knows" that something suspicious is happening?&lt;/p&gt;

&lt;p&gt;I just built a tool that does exactly that — and in this post, I'll explain every part of it in plain English. No security background needed. Let's go.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is This Project?
&lt;/h2&gt;

&lt;p&gt;Imagine you run a cloud storage website. Thousands of people visit it every day. Most of them are normal users uploading files, logging in, browsing around.&lt;/p&gt;

&lt;p&gt;But one day, someone decides to &lt;strong&gt;attack your website&lt;/strong&gt;. They write a script that sends thousands of requests per second from one IP address, trying to crash your server or sneak past your security. This is called a &lt;strong&gt;DDoS attack&lt;/strong&gt; (Distributed Denial of Service).&lt;/p&gt;

&lt;p&gt;My tool sits between the internet and the website, watches every single request coming in, and asks one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Does this look normal — or is something weird happening?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If something looks weird, it automatically &lt;strong&gt;blocks the attacker&lt;/strong&gt; and sends me a message on Slack.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Big Picture
&lt;/h2&gt;

&lt;p&gt;Here's the flow in simple terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Someone visits the website
        ↓
Nginx (the traffic cop) lets them through and writes a note about it
        ↓
My tool reads those notes in real time
        ↓
It checks: "Is this normal traffic, or a spike?"
        ↓
If it's a spike → Block the IP + Send Slack alert
If it's normal  → Do nothing, keep watching
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "notes" Nginx writes are called &lt;strong&gt;access logs&lt;/strong&gt; — one line per request, saved to a file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Reading the Log File (The Monitor)
&lt;/h2&gt;

&lt;p&gt;Every time someone visits the website, Nginx writes a line to a log file that looks like this:&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;"source_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"45.33.12.99"&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;"2025-04-25T10:32:01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/login"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"response_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1452&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;That's one visitor, at one moment in time.&lt;/p&gt;

&lt;p&gt;My tool opens that file and &lt;strong&gt;reads it line by line, forever&lt;/strong&gt; — like someone sitting next to a printer watching pages come out. In Python, this is called "tailing" a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/log/nginx/hng-access.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="c1"&gt;# Jump to the end (don't replay old history)
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# No new line yet, wait a tiny bit
&lt;/span&gt;            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# We got a new request!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, right? It just keeps checking for new lines forever.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: The Sliding Window (Counting Recent Requests)
&lt;/h2&gt;

&lt;p&gt;Now that we can read each request, we need to answer: &lt;strong&gt;"How many requests came in the last 60 seconds?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A naive approach would be to count all requests in the last minute. But that resets every 60 seconds and loses accuracy.&lt;/p&gt;

&lt;p&gt;Instead, I use a &lt;strong&gt;sliding window&lt;/strong&gt; — think of it like a moving spotlight that always shows you exactly the last 60 seconds.&lt;/p&gt;

&lt;p&gt;Here's the idea using a real-world analogy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Imagine a bouncer at a club keeping a paper roll. Every time someone enters, they write the time on the roll. Every few seconds, they tear off the old end (entries older than 60 seconds). The number of entries left = people who arrived in the last 60 seconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Python, I use a &lt;code&gt;deque&lt;/code&gt; (double-ended queue) — a list you can add to on the right and remove from the left efficiently:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Our "paper roll"
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_request&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Write the time on the roll
&lt;/span&gt;
    &lt;span class="c1"&gt;# Tear off old entries (older than 60 seconds)
&lt;/span&gt;    &lt;span class="n"&gt;cutoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;cutoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# How many requests in the last 60 seconds?
&lt;/span&gt;    &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;  &lt;span class="c1"&gt;# requests per second
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I maintain one of these windows &lt;strong&gt;per IP address&lt;/strong&gt; and one &lt;strong&gt;globally&lt;/strong&gt; (all IPs combined).&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: The Baseline (Learning What "Normal" Looks Like)
&lt;/h2&gt;

&lt;p&gt;Here's the clever part. We can't just say "more than 10 requests per second is an attack" — because maybe your website normally gets 50 requests per second at peak hours, and only 2 at night.&lt;/p&gt;

&lt;p&gt;So instead of hardcoding a number, my tool &lt;strong&gt;learns what normal looks like&lt;/strong&gt; over time.&lt;/p&gt;

&lt;p&gt;Every second, it records the current traffic rate. It keeps the last &lt;strong&gt;30 minutes&lt;/strong&gt; of these recordings. Every 60 seconds, it calculates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mean&lt;/strong&gt; — the average traffic rate over the last 30 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard deviation&lt;/strong&gt; — how much the traffic varies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like a teacher grading on a curve. If the average test score is 70, a score of 95 is unusually high. If everyone scores between 85–95, then 95 is totally normal.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]&lt;/span&gt;  &lt;span class="c1"&gt;# One sample per second, last 30 min
&lt;/span&gt;
&lt;span class="n"&gt;mean&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Average rate
&lt;/span&gt;&lt;span class="n"&gt;stddev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool also tracks traffic &lt;strong&gt;per hour&lt;/strong&gt; separately. So if rush hour is always busier, it compares against rush hour data — not the quiet overnight average.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Detecting the Attack (The Z-Score)
&lt;/h2&gt;

&lt;p&gt;Now we have the current rate and we know what "normal" looks like. How do we decide if something is an attack?&lt;/p&gt;

&lt;p&gt;I use a &lt;strong&gt;z-score&lt;/strong&gt; — a simple math formula that answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many standard deviations above normal is this?"&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;z_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_rate&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;stddev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;z_score &amp;gt; 3.0&lt;/code&gt;, that means the current rate is so far above normal that there's only a 0.1% chance it's a coincidence. That's our signal to act.&lt;/p&gt;

&lt;p&gt;I also have a backup rule: if the rate is &lt;strong&gt;more than 5 times the average&lt;/strong&gt;, it's flagged regardless of the z-score. Whichever rule fires first wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal traffic: mean = 5 req/s, stddev = 1.5&lt;/li&gt;
&lt;li&gt;Attacker sending: 50 req/s&lt;/li&gt;
&lt;li&gt;Z-score = (50 - 5) / 1.5 = &lt;strong&gt;30&lt;/strong&gt; → Way above 3.0 → 🚨 ATTACK DETECTED&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a bonus feature: if an IP is sending lots of &lt;strong&gt;error responses&lt;/strong&gt; (like hitting bad login attempts over and over), we tighten the thresholds even further for that IP specifically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Blocking the Attacker (iptables)
&lt;/h2&gt;

&lt;p&gt;Once we've decided an IP is an attacker, we need to &lt;strong&gt;stop them immediately&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; is a built-in Linux tool that acts like a firewall. You can tell it: "Drop all traffic from this IP address — don't even respond to them."&lt;/p&gt;

&lt;p&gt;My tool runs this command in Python:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ban_ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iptables&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-I&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INPUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-j&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DROP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One command. The attacker's packets are now silently dropped by the operating system — they never even reach the web server.&lt;/p&gt;

&lt;p&gt;You can verify it worked with:&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;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; INPUT &lt;span class="nt"&gt;-n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a line like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;DROP  all  &lt;span class="nt"&gt;--&lt;/span&gt;  45.33.12.99  0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 6: Auto-Unban (Giving Second Chances)
&lt;/h2&gt;

&lt;p&gt;What if we accidentally blocked a real user? Or the attacker stopped and wants to try again?&lt;/p&gt;

&lt;p&gt;My tool uses a &lt;strong&gt;backoff schedule&lt;/strong&gt; — bans get longer each time the same IP misbehaves:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offense&lt;/th&gt;
&lt;th&gt;Ban Duration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1st time&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2nd time&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3rd time&lt;/td&gt;
&lt;td&gt;2 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4th time&lt;/td&gt;
&lt;td&gt;Permanent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every 30 seconds, the tool checks: "Has anyone's ban expired?" If yes, it removes the iptables rule and sends a Slack notification.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 7: Slack Notifications
&lt;/h2&gt;

&lt;p&gt;Every time something happens — a ban, an unban, or a global traffic surge — my tool sends a message to a Slack channel so I know about it immediately.&lt;/p&gt;

&lt;p&gt;It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 IP BANNED
• IP: 45.33.12.99
• Condition: z-score=18.4
• Current rate: 120.00 req/s
• Baseline mean: 5.20 req/s
• Ban duration: 10 min
• Time: 2025-04-25T14:22:01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses Slack's "Incoming Webhooks" feature — you post a JSON message to a special URL, and it appears in your Slack channel.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 8: The Live Dashboard
&lt;/h2&gt;

&lt;p&gt;Finally, the whole system has a web dashboard you can open in your browser. It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current global requests per second&lt;/li&gt;
&lt;li&gt;The baseline mean and standard deviation&lt;/li&gt;
&lt;li&gt;CPU and memory usage of the server&lt;/li&gt;
&lt;li&gt;List of currently banned IPs&lt;/li&gt;
&lt;li&gt;Top 10 most active IP addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It refreshes automatically every 3 seconds so you always see what's happening live.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here's the whole system in one picture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                        ┌─────────────────────────┐
Internet requests  ───► │  Nginx (reverse proxy)  │
                        │  writes JSON log lines  │
                        └──────────┬──────────────┘
                                   │ log file
                        ┌──────────▼──────────────┐
                        │     Monitor Thread       │
                        │  tails log line by line  │
                        └──────────┬──────────────┘
                                   │ parsed request
                        ┌──────────▼──────────────┐
                        │    Detector Thread       │
                        │  z-score &amp;amp; rate check    │
                        └──────┬──────────┬────────┘
                               │          │
               ┌───────────────▼─┐    ┌───▼──────────────┐
               │  Blocker        │    │  Notifier         │
               │  iptables DROP  │    │  Slack alert      │
               └────────┬────────┘    └───────────────────┘
                        │
               ┌────────▼────────┐
               │  Unbanner       │
               │  removes rule   │
               │  after timeout  │
               └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every component runs in its own &lt;strong&gt;thread&lt;/strong&gt; — meaning they all run simultaneously, like workers in a factory each doing their own job at the same time.&lt;/p&gt;




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

&lt;p&gt;Building this taught me a few things I hadn't truly understood before:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security is about patterns, not rules.&lt;/strong&gt; The most powerful part of this system isn't the blocking — it's the baseline that learns what's normal. A hardcoded rule would break on the first unusual-but-legitimate traffic spike.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple data structures go a long way.&lt;/strong&gt; A deque + a timestamp is enough to build a production-grade sliding window. You don't need a database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linux tools are powerful.&lt;/strong&gt; &lt;code&gt;iptables&lt;/code&gt; has been in Linux for decades. One shell command blocks an IP at the kernel level — nothing the attacker sends can get through.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Want to Build It Yourself?
&lt;/h2&gt;

&lt;p&gt;The full code is available at: &lt;code&gt;https://github.com/ucheenyi/hng-detector&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The README has step-by-step setup instructions starting from a fresh Linux server.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If you found this helpful, drop a like or comment — I'd love to hear what you're building.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>UC Pocket Ubuntu OS: From 303MB to 80MB with Docker Optimization</title>
      <dc:creator>Uchechukwu Enyi </dc:creator>
      <pubDate>Thu, 17 Apr 2025 10:45:00 +0000</pubDate>
      <link>https://dev.to/uchemira/uc-pocket-ubuntu-os-from-303mb-to-80mb-with-docker-optimization-4d0g</link>
      <guid>https://dev.to/uchemira/uc-pocket-ubuntu-os-from-303mb-to-80mb-with-docker-optimization-4d0g</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As developers and DevOps engineers, we constantly seek ways to optimize our environments—reducing image sizes, improving security, and maintaining performance. In this article, I'll walk you through how I transformed a standard &lt;strong&gt;303MB Ubuntu Docker image&lt;/strong&gt; into a &lt;strong&gt;lean 80MB distroless-based container&lt;/strong&gt; while retaining full functionality (SSH, Apache, and persistent storage).  &lt;/p&gt;

&lt;p&gt;We'll cover:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Multi-stage builds&lt;/strong&gt; to eliminate unnecessary dependencies&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Distroless optimization&lt;/strong&gt; for security and size reduction&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Persistent storage&lt;/strong&gt; to retain data across container restarts&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;DevSecOps benefits&lt;/strong&gt; (smaller attack surface, faster CI/CD)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;How non-Linux users can replace VMs with this pocket Ubuntu&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;The Problem: Bloated Ubuntu Images&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Initially, my Dockerfile used a standard Ubuntu base with SSH and Apache:  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Original Dockerfile (303MB)&lt;/strong&gt;
&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; ubuntu:latest&lt;/span&gt;

&lt;span class="c"&gt;# Set environment variables&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ=Africa/Lagos&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LANG=en_US.UTF-8&lt;/span&gt;

&lt;span class="c"&gt;# Install packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    tzdata &lt;span class="se"&gt;\
&lt;/span&gt;    locales &lt;span class="se"&gt;\
&lt;/span&gt;    openssh-server &lt;span class="se"&gt;\
&lt;/span&gt;    apache2 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Configure timezone &amp;amp; locale&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-snf&lt;/span&gt; /usr/share/zoneinfo/&lt;span class="nv"&gt;$TZ&lt;/span&gt; /etc/localtime &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$TZ&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/timezone &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    locale-gen en_US.UTF-8

&lt;span class="c"&gt;# Set up SSH&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /var/run/sshd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'root:password'&lt;/span&gt; | chpasswd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/#PermitRootLogin prohibit-password/PermitRootLogin yes/'&lt;/span&gt; /etc/ssh/sshd_config

&lt;span class="c"&gt;# Expose ports&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 22 80&lt;/span&gt;

&lt;span class="c"&gt;# Persistent data volume&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /data

&lt;span class="c"&gt;# Keep container running&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'#!/bin/bash\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;service ssh start\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;service apache2 start\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;tail -f /dev/null'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /entrypoint.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /entrypoint.sh

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issues with this approach:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;303MB is too large&lt;/strong&gt; for a minimal Ubuntu environment&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Unnecessary packages&lt;/strong&gt; increase security risks&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;No multi-stage optimization&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;The Solution: Distroless + Multi-Stage Builds&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By using &lt;strong&gt;multi-stage builds&lt;/strong&gt; and &lt;strong&gt;distroless base images&lt;/strong&gt;, we reduce bloat while keeping essential functionality.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Optimized Dockerfile (80MB)&lt;/strong&gt;
&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;# Stage 1: Builder (Full Ubuntu)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ubuntu:22.04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="c"&gt;# Install only essentials&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    openssh-server &lt;span class="se"&gt;\
&lt;/span&gt;    apache2 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Configure SSH&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /var/run/sshd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'root:password'&lt;/span&gt; | chpasswd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/#PermitRootLogin prohibit-password/PermitRootLogin yes/'&lt;/span&gt; /etc/ssh/sshd_config

&lt;span class="c"&gt;# Stage 2: Distroless Runtime&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/base-debian11&lt;/span&gt;

&lt;span class="c"&gt;# Copy only necessary binaries&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/sbin/sshd /usr/sbin/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/sbin/apache2 /usr/sbin/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /bin/bash /bin/&lt;/span&gt;

&lt;span class="c"&gt;# Copy essential libraries&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/&lt;/span&gt;

&lt;span class="c"&gt;# Set up persistent storage&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;777 /data
&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; /data&lt;/span&gt;

&lt;span class="c"&gt;# Entrypoint script&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'#!/bin/bash\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;/usr/sbin/sshd -D &amp;amp;\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;/usr/sbin/apache2 -DFOREGROUND &amp;amp;\n&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s1"&gt;tail -f /dev/null'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /entrypoint.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /entrypoint.sh

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 22 80&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Key Optimizations&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✔ &lt;strong&gt;Multi-stage build&lt;/strong&gt; separates build and runtime dependencies&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Distroless base&lt;/strong&gt; removes unnecessary packages (no shell, no bloat)&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Only essential binaries&lt;/strong&gt; copied (SSH, Apache, Bash)&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Persistent &lt;code&gt;/data&lt;/code&gt; volume&lt;/strong&gt; for file storage  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;How to Use UC Pocket Ubuntu&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. First, Run the Container in Detached Mode&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 2222:22 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/ubuntu-data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-ubuntu &lt;span class="se"&gt;\&lt;/span&gt;
  ucheenyi/uc-pocket-ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;2. Then, Execute Commands Inside the Running Container&lt;/strong&gt;
&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;# Get an interactive bash shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-ubuntu bash

&lt;span class="c"&gt;# Or run single commands&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;my-ubuntu &lt;span class="nb"&gt;ls&lt;/span&gt; /data
docker &lt;span class="nb"&gt;exec &lt;/span&gt;my-ubuntu apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;3. Access Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSH&lt;/strong&gt;: &lt;code&gt;ssh root@localhost -p 2222&lt;/code&gt; (password: &lt;code&gt;password&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache&lt;/strong&gt;: Open &lt;code&gt;http://localhost:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Stop and Remove When Done&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop my-ubuntu
docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-ubuntu
&lt;span class="c"&gt;# Your data persists in ~/ubuntu-data on host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Matters for DevSecOps &amp;amp; DevOps&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🔹 &lt;strong&gt;Smaller Attack Surface&lt;/strong&gt; (No unnecessary packages = fewer CVEs)&lt;br&gt;&lt;br&gt;
🔹 &lt;strong&gt;Faster CI/CD Pipelines&lt;/strong&gt; (Smaller images = quicker deployments)&lt;br&gt;&lt;br&gt;
🔹 &lt;strong&gt;Cost-Efficient&lt;/strong&gt; (Replaces heavy VMs with lightweight containers)&lt;br&gt;&lt;br&gt;
🔹 &lt;strong&gt;Cross-Platform&lt;/strong&gt; (Windows/Mac users get full Ubuntu CLI without VMs)  &lt;/p&gt;




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

&lt;p&gt;By applying &lt;strong&gt;multi-stage builds&lt;/strong&gt; and &lt;strong&gt;distroless optimization&lt;/strong&gt;, we reduced the image size by &lt;strong&gt;73%&lt;/strong&gt; while keeping all functionality.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Dockerhub link for the image:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://hub.docker.com/r/ucheenyi/uc-pocket-ubuntu" rel="noopener noreferrer"&gt;https://hub.docker.com/r/ucheenyi/uc-pocket-ubuntu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach is perfect for:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Developers&lt;/strong&gt; needing a lightweight Ubuntu environment&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;DevOps teams&lt;/strong&gt; optimizing CI/CD pipelines&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Security teams&lt;/strong&gt; minimizing attack surfaces  &lt;/p&gt;




</description>
    </item>
    <item>
      <title>Implementing GitOps with FluxCD for Kubernetes Applications</title>
      <dc:creator>Uchechukwu Enyi </dc:creator>
      <pubDate>Sun, 13 Apr 2025 20:16:50 +0000</pubDate>
      <link>https://dev.to/uchemira/implementing-gitops-with-fluxcd-for-kubernetes-applications-3mfa</link>
      <guid>https://dev.to/uchemira/implementing-gitops-with-fluxcd-for-kubernetes-applications-3mfa</guid>
      <description>&lt;p&gt;This guide provides a clear, standardized process for configuring FluxCD to manage Kubernetes applications using GitOps principles. It automates the deployment of application manifests from a Git repository to a &lt;code&gt;staging&lt;/code&gt; namespace, utilizing a streamlined folder structure for simplicity and efficiency. The guide includes practical tests to validate GitOps functionality through an image tag update and pruning via replica adjustments, while preserving local Git integration for robust configuration management.&lt;/p&gt;

&lt;h4&gt;
  
  
  Prerequisites
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Cluster&lt;/strong&gt;: A running cluster, such as Kind, Minikube, AKS, or EKS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kubectl&lt;/strong&gt;: Installed and configured to interact with the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flux CLI&lt;/strong&gt;: Installed (e.g., &lt;code&gt;curl -s https://fluxcd.io/install.sh | bash&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt;: Installed for managing repositories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Account&lt;/strong&gt;: Maintaining two repositories:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;github-username&amp;gt;/flux-config&lt;/code&gt;: Stores Flux configurations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;github-username&amp;gt;/app-repo&lt;/code&gt;: Contains application manifests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;GitHub Personal Access Token (PAT)&lt;/strong&gt;: Configured with &lt;code&gt;repo&lt;/code&gt; scope permissions.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Working Directory&lt;/strong&gt;: &lt;code&gt;/flux-config&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 1: Set Up the Environment
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create Working Directory&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&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; /flux-config
   &lt;span class="nb"&gt;cd&lt;/span&gt; /flux-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialize Git Repository&lt;/strong&gt;:
Set up a local Git repository for configuration management:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git init
   git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure GitHub Credentials&lt;/strong&gt;:
Set environment variables for authentication, replacing &lt;code&gt;&amp;lt;github-username&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;your-token&amp;gt;&lt;/code&gt; with your GitHub credentials:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;github-username&amp;gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Cluster Compatibility&lt;/strong&gt;:
Confirm the cluster is ready for Flux:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect confirmation of &lt;code&gt;kubectl&lt;/code&gt; (version 1.26 or higher), cluster connectivity, and Flux CLI readiness.&lt;/li&gt;
&lt;li&gt;Resolve issues, such as setting &lt;code&gt;kubeconfig&lt;/code&gt; with &lt;code&gt;kubectl config use-context &amp;lt;context&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 2: Create Folder Structure
&lt;/h4&gt;

&lt;p&gt;The folder structure is minimal, including only critical configuration files, with &lt;code&gt;flux-system/&lt;/code&gt; generated during setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/flux-config/
├── .git/
├── clusters/
│   ├── staging/
│   │   ├── app-repo.yaml
│   │   └── apps.yaml
├── flux-system/ (auto-generated)
│   ├── gotk-components.yaml
│   ├── gotk-sync.yaml
│   └── kustomization.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Create directories:
&lt;/li&gt;
&lt;/ol&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; clusters/staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create configuration files:
&lt;/li&gt;
&lt;/ol&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;clusters/staging/app-repo.yaml clusters/staging/apps.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3: Configure Resources
&lt;/h4&gt;

&lt;p&gt;Define resources to monitor the application’s Git repository and deploy its manifests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Edit &lt;code&gt;clusters/staging/app-repo.yaml&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;source.toolkit.fluxcd.io/v1&lt;/span&gt;
   &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepository&lt;/span&gt;
   &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
     &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flux-system&lt;/span&gt;
   &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;
     &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/&amp;lt;github-username&amp;gt;/app-repo&lt;/span&gt;
     &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Instructs Flux to check the repository every minute for changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Edit &lt;code&gt;clusters/staging/apps.yaml&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomize.toolkit.fluxcd.io/v1&lt;/span&gt;
   &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kustomization&lt;/span&gt;
   &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
     &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flux-system&lt;/span&gt;
   &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
     &lt;span class="na"&gt;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitRepository&lt;/span&gt;
       &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
     &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./kubernetes/manifests&lt;/span&gt;
     &lt;span class="na"&gt;prune&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
     &lt;span class="na"&gt;targetNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Configures Flux to apply manifests from &lt;code&gt;kubernetes/manifests/&lt;/code&gt; to the &lt;code&gt;staging&lt;/code&gt; namespace, with pruning enabled to remove undefined resources, reconciling every five minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 4: Commit Configurations
&lt;/h4&gt;

&lt;p&gt;Add and commit configurations to the local repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add clusters/staging/
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add GitRepository and Kustomization for my-app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 5: Bootstrap Flux
&lt;/h4&gt;

&lt;p&gt;Initialize Flux to deploy its controllers and synchronize configurations with GitHub.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run Bootstrap&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flux bootstrap github &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_USER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--repository&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;flux-config &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clusters/staging &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--personal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Creates or updates &lt;code&gt;&amp;lt;github-username&amp;gt;/flux-config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Installs Flux controllers (e.g., &lt;code&gt;source-controller&lt;/code&gt;, &lt;code&gt;kustomize-controller&lt;/code&gt;) in the &lt;code&gt;flux-system&lt;/code&gt; namespace.&lt;/li&gt;
&lt;li&gt;Generates &lt;code&gt;flux-system/&lt;/code&gt; with &lt;code&gt;gotk-components.yaml&lt;/code&gt;, &lt;code&gt;gotk-sync.yaml&lt;/code&gt;, and &lt;code&gt;kustomization.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pushes configurations to GitHub and sets Flux to monitor &lt;code&gt;clusters/staging/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Setup&lt;/strong&gt;:
Ensure Flux components are running:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Confirm pods like &lt;code&gt;source-controller-...&lt;/code&gt; and &lt;code&gt;kustomize-controller-...&lt;/code&gt; are active.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 6: Configure Application Manifests
&lt;/h4&gt;

&lt;p&gt;Set up manifests in the application repository to define Kubernetes resources.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone Application Repository&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&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; /
   &lt;span class="nb"&gt;mkdir &lt;/span&gt;temp-app
   &lt;span class="nb"&gt;cd &lt;/span&gt;temp-app
   git clone https://github.com/&amp;lt;github-username&amp;gt;/app-repo.git
   &lt;span class="nb"&gt;cd &lt;/span&gt;app-repo
   &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; kubernetes/manifests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create &lt;code&gt;kubernetes/manifests/deployment.yaml&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create &lt;code&gt;kubernetes/manifests/service.yaml&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commit and Push&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git add &lt;span class="nb"&gt;.&lt;/span&gt;
   git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add my-app manifests"&lt;/span&gt;
   git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 7: Create Namespace
&lt;/h4&gt;

&lt;p&gt;Ensure the &lt;code&gt;staging&lt;/code&gt; namespace is present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace staging &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 8: Verify Deployment
&lt;/h4&gt;

&lt;p&gt;Confirm Flux has correctly applied the manifests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check GitRepository&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flux get sources git my-app &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect &lt;code&gt;Ready: True&lt;/code&gt;, indicating repository sync.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check Kustomization&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flux get kustomizations my-app &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect &lt;code&gt;Ready: True&lt;/code&gt;, confirming manifest deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Resources&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl get deployments,services &lt;span class="nt"&gt;-n&lt;/span&gt; staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect a &lt;code&gt;my-app&lt;/code&gt; deployment (1 replica, &lt;code&gt;nginx:1.14.2&lt;/code&gt;) and &lt;code&gt;my-app-service&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 9: Test GitOps with Image Tag Update
&lt;/h4&gt;

&lt;p&gt;Validate GitOps by updating the application’s image tag and confirming Flux applies the change.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update Image Tag&lt;/strong&gt;:
Edit &lt;code&gt;/temp-app/app-repo/kubernetes/manifests/deployment.yaml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commit and Push&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&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; /temp-app/app-repo
   git add kubernetes/manifests/deployment.yaml
   git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update my-app image to nginx:1.18.0"&lt;/span&gt;
   git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reconcile (Optional)&lt;/strong&gt;:
Accelerate the update:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flux reconcile kustomization my-app &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Update&lt;/strong&gt;:
Inspect running pods:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; staging &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect new pods running &lt;code&gt;nginx:1.18.0&lt;/code&gt;, with old &lt;code&gt;nginx:1.14.2&lt;/code&gt; pods terminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 10: Test Pruning with Replica Adjustment
&lt;/h4&gt;

&lt;p&gt;Ensure pruning maintains the Git-defined state by introducing and correcting a configuration drift.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Increase Replicas Manually&lt;/strong&gt;:
Modify the deployment:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl scale deployment my-app &lt;span class="nt"&gt;-n&lt;/span&gt; staging &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Increase&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl get deployment my-app &lt;span class="nt"&gt;-n&lt;/span&gt; staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect 3 replicas.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reconcile&lt;/strong&gt;:
With &lt;code&gt;deployment.yaml&lt;/code&gt; specifying &lt;code&gt;replicas: 1&lt;/code&gt;, Flux will revert the change:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flux reconcile kustomization my-app &lt;span class="nt"&gt;-n&lt;/span&gt; flux-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Pruning&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl get deployment my-app &lt;span class="nt"&gt;-n&lt;/span&gt; staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Expect 1 replica, confirming Git state enforcement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 11: Troubleshoot
&lt;/h4&gt;

&lt;p&gt;Address issues to ensure a reliable GitOps pipeline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap Issues&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Verify &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; has &lt;code&gt;repo&lt;/code&gt; scope: &lt;code&gt;curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Confirm network access and repository settings.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;GitRepository Problems&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Validate repository URL and branch.&lt;/li&gt;
&lt;li&gt;Check status: &lt;code&gt;kubectl describe gitrepository my-app -n flux-system&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Kustomization Errors&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Ensure &lt;code&gt;path: ./kubernetes/manifests&lt;/code&gt; exists in &lt;code&gt;app-repo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Review logs: &lt;code&gt;kubectl logs -n flux-system -l app=kustomize-controller&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Update or Pruning Failures&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;prune: true&lt;/code&gt; in &lt;code&gt;apps.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Inspect events: &lt;code&gt;kubectl describe kustomization my-app -n flux-system&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  FluxCD: Transforming Kubernetes with GitOps
&lt;/h3&gt;

&lt;p&gt;FluxCD redefines Kubernetes application management by leveraging &lt;strong&gt;GitOps&lt;/strong&gt;, &lt;strong&gt;DevSecOps&lt;/strong&gt;, &lt;strong&gt;CD pipeline&lt;/strong&gt;, and &lt;strong&gt;DevOps&lt;/strong&gt; principles. With a compact &lt;code&gt;/flux-config&lt;/code&gt; structure, it automates deployments, ensuring consistency, security, and collaboration for any application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitOps&lt;/strong&gt;: FluxCD establishes Git as the single source of truth, synchronizing manifests from &lt;code&gt;&amp;lt;github-username&amp;gt;/app-repo&lt;/code&gt;. Updating an image from &lt;code&gt;nginx:1.14.2&lt;/code&gt; to &lt;code&gt;1.18.0&lt;/code&gt; illustrates automated deployment, while pruning replicas from 3 to 1 enforces the declared state, enhancing traceability and control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DevSecOps&lt;/strong&gt;: Security is integrated through Git’s access controls and Flux’s pull-based synchronization, reducing manual cluster interactions. Declarative configurations enable early validation, supporting DevSecOps’ emphasis on secure development and operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CD Pipeline&lt;/strong&gt;: FluxCD’s continuous polling drives seamless updates, automating changes like image tag updates without manual intervention, accelerating delivery for modern applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DevOps&lt;/strong&gt;: Centralized Git configurations unite development and operations teams, while automated synchronization and pruning minimize operational effort, reflecting DevOps’ focus on collaboration and efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FluxCD delivers a robust, secure, and automated GitOps framework, empowering teams to manage Kubernetes applications with confidence and precision.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>gitops</category>
      <category>devsecops</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Uchechukwu Enyi </dc:creator>
      <pubDate>Thu, 10 Apr 2025 13:49:25 +0000</pubDate>
      <link>https://dev.to/uchemira/-nno</link>
      <guid>https://dev.to/uchemira/-nno</guid>
      <description></description>
      <category>emptystring</category>
    </item>
  </channel>
</rss>
