<?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: Miracle Olorunsola</title>
    <description>The latest articles on Forem by Miracle Olorunsola (@techgirli).</description>
    <link>https://forem.com/techgirli</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%2F3686248%2F7ca0fab5-f420-43c9-a32d-91451262afd1.jpeg</url>
      <title>Forem: Miracle Olorunsola</title>
      <link>https://forem.com/techgirli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/techgirli"/>
    <language>en</language>
    <item>
      <title>How I Took a Broken Microservices App to Production with Docker &amp; CI/CD</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Thu, 30 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/how-i-took-a-broken-microservices-app-to-production-with-docker-cicd-1d78</link>
      <guid>https://forem.com/techgirli/how-i-took-a-broken-microservices-app-to-production-with-docker-cicd-1d78</guid>
      <description>&lt;h1&gt;
  
  
  The Challenge
&lt;/h1&gt;

&lt;p&gt;I was given a multi-service application with:&lt;/p&gt;

&lt;p&gt;A Node.js frontend&lt;br&gt;
A FastAPI backend&lt;br&gt;
A Python worker&lt;br&gt;
Redis as a queue&lt;/p&gt;

&lt;p&gt;The catch?&lt;/p&gt;

&lt;p&gt;It was intentionally broken.&lt;/p&gt;

&lt;p&gt;My task wasn’t to build it was to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix it&lt;/li&gt;
&lt;li&gt;Containerize it&lt;/li&gt;
&lt;li&gt;Ship it with a full CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Step 1: Debugging the System&lt;/code&gt;&lt;br&gt;
Before touching Docker, I read the entire codebase.&lt;/p&gt;

&lt;p&gt;Issues I found:&lt;br&gt;
Hardcoded localhost breaking container networking&lt;br&gt;
Missing environment variables&lt;br&gt;
Redis connection failures&lt;br&gt;
API crashing on startup&lt;br&gt;
Frontend calling wrong endpoints&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Fix approach:&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replaced hardcoded values with env variables&lt;br&gt;
Standardized service communication (api, redis)&lt;br&gt;
Added proper error handling&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Step 2: Containerization&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Each service got its own Dockerfile.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Key decisions:&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used python:3.11-alpine for minimal size&lt;/li&gt;
&lt;li&gt;Created non-root users (appuser)&lt;/li&gt;
&lt;li&gt;Added HEALTHCHECK to every service&lt;/li&gt;
&lt;li&gt;Avoided copying .env files into images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Step 3: Docker Compose Setup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This was where things got interesting.&lt;/p&gt;

&lt;p&gt;What I implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal bridge network&lt;/li&gt;
&lt;li&gt;Redis hidden from host&lt;/li&gt;
&lt;li&gt;depends_on with health conditions&lt;/li&gt;
&lt;li&gt;Resource limits for all containers
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;depends_on:
  redis:
    condition: service_healthy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Step 4: CI/CD Pipeline&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I built a GitHub Actions pipeline with strict stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Lint
flake8 (Python)
eslint (Node)
hadolint (Docker)
Test
pytest with mocked Redis
coverage report upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Build
Tagged images with SHA + latest
Pushed to local registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Security Scan
Trivy scan
Fail on CRITICAL vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Integration Test
Spin up full stack
Submit job → poll → verify completion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Deploy
Rolling update
Health check gating
Automatic rollback on failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Challenges I Faced
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Docker daemon not running (blocked everything)&lt;/li&gt;
&lt;li&gt;ESLint breaking due to Node version mismatch&lt;/li&gt;
&lt;li&gt;Trivy failing due to missing images&lt;/li&gt;
&lt;li&gt;Race conditions between services&lt;/li&gt;
&lt;li&gt;Containers “starting” but not “ready”&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Key Takeaways
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Health checks &amp;gt; startup order&lt;/li&gt;
&lt;li&gt;Containers must be self-sufficient&lt;/li&gt;
&lt;li&gt;CI/CD pipelines should fail fast
&lt;strong&gt;DevOps is about system thinking, not just tools&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Techgirli/hng14-stage2-devops/actions/runs/24858972851" rel="noopener noreferrer"&gt;Repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fceh6n5771zhmp97csged.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fceh6n5771zhmp97csged.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyk6lapa67ixzgan3ts6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyk6lapa67ixzgan3ts6.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn04ncd041fbqz4dv1rls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn04ncd041fbqz4dv1rls.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>deploymenent</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>How I Built a Real-Time DDoS Detection Engine with Python, Docker, and iptablesTags: devops, python, security, docker</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Wed, 29 Apr 2026 01:15:42 +0000</pubDate>
      <link>https://forem.com/techgirli/how-i-built-a-real-time-ddos-detection-engine-with-python-docker-and-iptablestags-devops-417g</link>
      <guid>https://forem.com/techgirli/how-i-built-a-real-time-ddos-detection-engine-with-python-docker-and-iptablestags-devops-417g</guid>
      <description>&lt;h1&gt;
  
  
  How I Built a Real-Time DDoS Detection Engine with Python, Docker, and iptables
&lt;/h1&gt;

&lt;p&gt;Have you ever wondered how websites protect themselves from attackers &lt;br&gt;
who send millions of requests trying to crash the server? That is called &lt;br&gt;
a DDoS attack (Distributed Denial of Service), and in this post I will &lt;br&gt;
show you how I built a system that detects and blocks these attacks &lt;br&gt;
automatically — in real time.&lt;/p&gt;

&lt;p&gt;This project was built as part of the HNG DevOps Stage 3 task. We were &lt;br&gt;
asked to protect a Nextcloud cloud storage platform running on Docker &lt;br&gt;
from suspicious traffic — without using any existing security tools like &lt;br&gt;
Fail2Ban. Everything had to be built from scratch.&lt;/p&gt;


&lt;h2&gt;
  
  
  What the Project Does and Why It Matters
&lt;/h2&gt;

&lt;p&gt;Imagine you run a shop. Normally 10 customers walk in per minute. &lt;br&gt;
Suddenly 5,000 people rush in at the same time — not to buy anything, &lt;br&gt;
but just to block the door so real customers cannot enter. That is &lt;br&gt;
exactly what a DDoS attack does to a web server.&lt;/p&gt;

&lt;p&gt;Our detection engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watches every HTTP request coming into the server in real time&lt;/li&gt;
&lt;li&gt;Learns what "normal" traffic looks like&lt;/li&gt;
&lt;li&gt;Detects when traffic suddenly spikes far above normal&lt;/li&gt;
&lt;li&gt;Automatically blocks the attacking IP address&lt;/li&gt;
&lt;li&gt;Sends an alert to Slack so you know what happened&lt;/li&gt;
&lt;li&gt;Automatically unblocks the IP after a set time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system runs as a background daemon meaning it runs continuously &lt;br&gt;
24/7 alongside your application, always watching.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;Here is how all the pieces fit together:&lt;/p&gt;

&lt;p&gt;Internet Traffic&lt;br&gt;
↓&lt;br&gt;
Nginx (reverse proxy)&lt;br&gt;
↓ writes JSON logs&lt;br&gt;
Shared Docker Volume (HNG-nginx-logs)&lt;br&gt;
↓ reads logs&lt;br&gt;
Detection Daemon (Python)&lt;br&gt;
↓              ↓              ↓&lt;br&gt;
iptables        Slack          Dashboard&lt;br&gt;
(block IP)     (alert)        (metrics UI)&lt;/p&gt;

&lt;p&gt;We use Docker Compose to run everything together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MariaDB&lt;/strong&gt; — database for Nextcloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nextcloud&lt;/strong&gt; — the cloud storage application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; — reverse proxy that logs all traffic in JSON format&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detector&lt;/strong&gt; — our Python daemon that watches the logs&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  How the Sliding Window Works
&lt;/h2&gt;

&lt;p&gt;This is the heart of the detection system. We need to know how many &lt;br&gt;
requests came from each IP address in the &lt;strong&gt;last 60 seconds&lt;/strong&gt; at any &lt;br&gt;
given moment.&lt;/p&gt;

&lt;p&gt;The naive approach would be to count requests per minute. But that has &lt;br&gt;
a problem — if someone sends 1000 requests at 11:59 and 1000 more at &lt;br&gt;
12:00, a per-minute counter would show two separate spikes of 1000 &lt;br&gt;
instead of the real burst of 2000.&lt;/p&gt;

&lt;p&gt;We solve this with a &lt;strong&gt;sliding window&lt;/strong&gt; using Python's &lt;code&gt;deque&lt;/code&gt; &lt;br&gt;
(double-ended queue):&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="c1"&gt;# One deque per IP address
&lt;/span&gt;&lt;span class="n"&gt;ip_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="n"&gt;WINDOW_SECONDS&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;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;ip_window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&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;timestamp&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="n"&gt;WINDOW_SECONDS&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 1: Add the new request timestamp
&lt;/span&gt;    &lt;span class="n"&gt;ip_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;# Step 2: Remove requests older than 60 seconds
&lt;/span&gt;    &lt;span class="c1"&gt;# popleft() removes from the left — O(1) operation
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;ip_window&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ip_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;ip_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;# Step 3: Count = requests in last 60 seconds
&lt;/span&gt;    &lt;span class="n"&gt;current_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;ip_window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current_rate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;deque&lt;/code&gt; is like a list but optimized for adding to one end and &lt;br&gt;
removing from the other. This makes the eviction of old entries very &lt;br&gt;
fast — O(1) — no matter how many requests are in the window.&lt;/p&gt;

&lt;p&gt;We maintain two windows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One per IP address&lt;/strong&gt; — to detect a single aggressive attacker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One global&lt;/strong&gt; — to detect a coordinated attack from many IPs&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  How the Baseline Learns from Traffic
&lt;/h2&gt;

&lt;p&gt;We cannot hardcode a threshold like "100 requests per second is too &lt;br&gt;
many." Maybe your server normally gets 500 per second at peak hours, &lt;br&gt;
or just 5 per second at 3 AM. A hardcoded value would cause false &lt;br&gt;
alarms constantly.&lt;/p&gt;

&lt;p&gt;Instead we use a &lt;strong&gt;rolling baseline&lt;/strong&gt; — the system learns what normal &lt;br&gt;
looks like from recent traffic history.&lt;/p&gt;

&lt;p&gt;Every second we record how many requests arrived:&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;math&lt;/span&gt;

&lt;span class="c1"&gt;# Keep 30 minutes of per-second counts
# 30 minutes × 60 seconds = 1800 slots
&lt;/span&gt;&lt;span class="n"&gt;per_second_counts&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="n"&gt;maxlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Every second:
&lt;/span&gt;&lt;span class="n"&gt;per_second_counts&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;requests_this_second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Every 60 seconds, recalculate mean and stddev:
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recalculate_baseline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;n&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;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;  &lt;span class="c1"&gt;# floor values
&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;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="n"&gt;variance&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;c&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;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Apply floors to prevent divide-by-zero
&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;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stddev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stddev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&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;We also maintain &lt;strong&gt;per-hour slots&lt;/strong&gt;. If the current hour has at least &lt;br&gt;
10 minutes of data, we prefer its statistics over the global 30-minute &lt;br&gt;
window. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At 9 AM (busy hour): baseline reflects busy traffic → higher 
threshold&lt;/li&gt;
&lt;li&gt;At 3 AM (quiet hour): baseline reflects quiet traffic → lower 
threshold&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system is always comparing current traffic to &lt;strong&gt;recent&lt;/strong&gt; traffic, &lt;br&gt;
not some fixed value set months ago.&lt;/p&gt;


&lt;h2&gt;
  
  
  How the Detection Logic Makes a Decision
&lt;/h2&gt;

&lt;p&gt;Once we have the current rate and the baseline, we check two conditions. &lt;br&gt;
Whichever fires first triggers an alert:&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;def&lt;/span&gt; &lt;span class="nf"&gt;check_anomaly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stddev&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Condition 1: Rate multiplier
&lt;/span&gt;    &lt;span class="c1"&gt;# Is traffic more than 5x the normal average?
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;5.0&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="k"&gt;return&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;rate&amp;gt;5x_mean(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Condition 2: Z-score
&lt;/span&gt;    &lt;span class="c1"&gt;# How many standard deviations above normal is this?
&lt;/span&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;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;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;z_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;zscore&amp;gt;3.0(z=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;z_score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# Normal traffic
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Z-score&lt;/strong&gt; measures how unusual something is statistically. A z-score &lt;br&gt;
of 3.0 means the current rate is 3 standard deviations above the mean &lt;br&gt;
— this happens by random chance less than 0.3% of the time. In other &lt;br&gt;
words, it is almost certainly an attack.&lt;/p&gt;

&lt;p&gt;We also have &lt;strong&gt;error surge detection&lt;/strong&gt;. If an IP generates lots of &lt;br&gt;
4xx/5xx errors (like failed login attempts), we tighten the thresholds &lt;br&gt;
automatically — making the system more sensitive to that specific IP.&lt;/p&gt;


&lt;h2&gt;
  
  
  How iptables Blocks an IP
&lt;/h2&gt;

&lt;p&gt;iptables is a firewall built into Linux. When we detect an attack from &lt;br&gt;
a specific IP, we add a DROP rule that tells the Linux kernel to silently &lt;br&gt;
discard all packets from that IP before they even reach Nginx:&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_address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Insert DROP rule at the TOP of the INPUT chain
&lt;/span&gt;    &lt;span class="c1"&gt;# -I means insert (top priority)
&lt;/span&gt;    &lt;span class="c1"&gt;# -s means source IP
&lt;/span&gt;    &lt;span class="c1"&gt;# -j DROP means silently discard the packet
&lt;/span&gt;    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&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;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_address&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="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="n"&gt;cmd&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;Banned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip_address&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;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unban_ip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# -D means delete the rule
&lt;/span&gt;    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&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;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;-D&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_address&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="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="n"&gt;cmd&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;Unbanned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip_address&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;The ban happens within &lt;strong&gt;10 seconds&lt;/strong&gt; of detection. The attacker's &lt;br&gt;
connection is simply dropped at the operating system level — they get &lt;br&gt;
no response at all, which is more efficient than sending error messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-unban backoff schedule:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1st offence → banned for 10 minutes&lt;/li&gt;
&lt;li&gt;2nd offence → banned for 30 minutes
&lt;/li&gt;
&lt;li&gt;3rd offence → banned for 2 hours&lt;/li&gt;
&lt;li&gt;4th offence → permanently banned&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Slack Alerts
&lt;/h2&gt;

&lt;p&gt;Every significant event sends a notification to a Slack channel:&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;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_slack_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhook_url&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;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stddev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&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;attachments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;color&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;#FF3B30&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;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;:rotating_light: *IP BAN TRIGGERED*&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;&amp;gt;*IP:* `&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="s"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;&amp;gt;*Condition:* `&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;&amp;gt;*Current rate:* &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; req/60s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;&amp;gt;*Baseline:* mean=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; stddev=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;stddev&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get alerts for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚨 IP banned (with condition, rate, baseline, duration)&lt;/li&gt;
&lt;li&gt;🔓 IP unbanned (with ban history)&lt;/li&gt;
&lt;li&gt;⚠️ Global traffic anomaly (when the whole site is under attack)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Live Dashboard
&lt;/h2&gt;

&lt;p&gt;We built a web dashboard using Flask that refreshes every 3 seconds &lt;br&gt;
showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Currently banned IPs and time remaining&lt;/li&gt;
&lt;li&gt;Global requests per second&lt;/li&gt;
&lt;li&gt;Top 10 source IPs&lt;/li&gt;
&lt;li&gt;CPU and memory usage&lt;/li&gt;
&lt;li&gt;Current baseline mean and standard deviation&lt;/li&gt;
&lt;li&gt;System uptime&lt;/li&gt;
&lt;li&gt;Baseline history graph&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Audit Log
&lt;/h2&gt;

&lt;p&gt;Every ban, unban, and baseline recalculation is written to a structured &lt;br&gt;
log file:  [2026-04-28T20:45:26Z] BAN ip=172.19.0.1 | condition=zscore&amp;gt;3.0(z=4.00) | rate=3.0 | baseline=1.00±0.50 | duration=600s&lt;br&gt;
[2026-04-28T20:55:26Z] UNBAN ip=172.19.0.1 | condition=auto_expire | duration_was=600s&lt;br&gt;
[2026-04-28T21:00:00Z] BASELINE_RECALC ip=global | baseline=17.14±21.50 | source=global_30min(n=64) &lt;/p&gt;




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

&lt;p&gt;Building this from scratch taught me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Statistics matter in security&lt;/strong&gt; — z-scores and standard deviation &lt;br&gt;
are not just academic concepts. They are practical tools for anomaly &lt;br&gt;
detection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adaptive baselines beat fixed thresholds&lt;/strong&gt; — a system that learns &lt;br&gt;
is far more accurate than one with hardcoded values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker networking is complex&lt;/strong&gt; — getting containers to communicate &lt;br&gt;
correctly took more troubleshooting than the detection logic itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;iptables is powerful&lt;/strong&gt; — blocking at the kernel level is the most &lt;br&gt;
efficient way to stop an attack. The attacker gets no response, &lt;br&gt;
wasting their resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always whitelist your own IP&lt;/strong&gt; — learned this the hard way when &lt;br&gt;
I locked myself out of my own server!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.12&lt;/strong&gt; — detection daemon&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flask&lt;/strong&gt; — dashboard web server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker + Docker Compose&lt;/strong&gt; — container orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; — reverse proxy with JSON logging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MariaDB&lt;/strong&gt; — database for Nextcloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nextcloud&lt;/strong&gt; — the application being protected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iptables&lt;/strong&gt; — IP blocking at kernel level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; — alert notifications&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The full source code is available at:&lt;br&gt;
&lt;a href="https://github.com/Techgirli/ddos-detector" rel="noopener noreferrer"&gt;https://github.com/Techgirli/ddos-detector&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;Building a security tool from scratch is one of the best ways to &lt;br&gt;
understand how attacks work and how defenses are designed. Every piece &lt;br&gt;
of this system — the sliding window, the adaptive baseline, the &lt;br&gt;
statistical detection, the automatic blocking — solves a real problem &lt;br&gt;
that security engineers face every day.&lt;/p&gt;

&lt;p&gt;If you have questions or want to build something similar, drop a comment &lt;br&gt;
below!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>From Zero to Production: My HNG DevOps Stage 0 Journey</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Mon, 20 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/from-zero-to-production-my-hng-devops-stage-0-journey-250j</link>
      <guid>https://forem.com/techgirli/from-zero-to-production-my-hng-devops-stage-0-journey-250j</guid>
      <description>&lt;p&gt;I just completed a hands-on Linux server setup and deployment no Docker, no automation, just raw DevOps fundamentals.&lt;/p&gt;

&lt;p&gt;Here’s exactly how I approached it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Provisioning &amp;amp; Access&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Spun up a Linux server on the cloud&lt;br&gt;
Connected via SSH using key-based authentication&lt;br&gt;
Created a secure non-root user: hngdevops&lt;br&gt;
Granted sudo privileges&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security Hardening&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Disabled root SSH login&lt;br&gt;
Disabled password authentication (keys only)&lt;br&gt;
Configured Uncomplicated Firewall:&lt;br&gt;
Allowed only ports 22 (SSH), 80 (HTTP), 443 (HTTPS)&lt;br&gt;
Blocked everything else&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Web Server Setup&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Installed and started Nginx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Configured&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;routes:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;displaying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HNG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;username&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;/api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response:&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HNGI14 Stage 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"track"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DevOps"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-username"&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSL Configuration&lt;/strong&gt;
&lt;code&gt;Used Certbot to generate a valid SSL certificate
Enabled HTTPS on the server
Configured HTTP → HTTPS redirect (301)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5.** Validation &amp;amp; Debugging**&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ensured:
/api returns HTTP 200 with Content-Type: application/json
Username matches exactly (case-sensitive)
SSL is valid (no self-signed certificates)
Firewall and Nginx are active and correctly configured

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Lessons&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Most failures aren’t tools they’re mis-configurations (ports, services, permissions)&lt;br&gt;
“Connection refused” vs “Timeout” tells you exactly where the issue is&lt;br&gt;
Security first: lock down access before exposing your server&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Live Demo

* Deployed and accessible over HTTPS 
This project sharpened my troubleshooting, Linux, and networking skills exactly what real-world DevOps demands.
hashtag#DevOps hashtag#Linux hashtag#Nginx hashtag#CyberSecurity hashtag#Cloud hashtag#LetsEncrypt hashtag#HNG hashtag#TechJourney

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>devops</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>HNG Stage 1 DevOps Task Completed — From Infrastructure to API Deployment</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Mon, 20 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/hng-stage-1-devops-task-completed-from-infrastructure-to-api-deployment-12ji</link>
      <guid>https://forem.com/techgirli/hng-stage-1-devops-task-completed-from-infrastructure-to-api-deployment-12ji</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68rzgrm9gv4jcnfb7975.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68rzgrm9gv4jcnfb7975.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After successfully setting up a Linux server and configuring Nginx in Stage 0, I took things further in Stage 1 by building and deploying a minimal backend API — focusing not just on development, but on how services run in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Built&lt;/strong&gt;&lt;br&gt;
A lightweight API with 3 endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; → Confirms the API is running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/health&lt;/code&gt; → Health check endpoint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/me&lt;/code&gt; → Returns my profile details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All endpoints:&lt;br&gt;
✔ Return JSON (&lt;code&gt;application/json&lt;/code&gt;)&lt;br&gt;
✔ Respond with HTTP 200&lt;br&gt;
✔ Optimized for &amp;lt;500ms response time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisioned a cloud VPS&lt;/li&gt;
&lt;li&gt;Ran the app on a &lt;strong&gt;non-public port&lt;/strong&gt; (security best practice)&lt;/li&gt;
&lt;li&gt;Configured &lt;strong&gt;Nginx as a reverse proxy&lt;/strong&gt; to handle public traffic&lt;/li&gt;
&lt;li&gt;Ensured high availability using a persistent service (&lt;code&gt;systemd&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Learning&lt;/strong&gt;&lt;br&gt;
As a DevOps engineer, deploying isn’t just about infrastructure — it’s about understanding the application lifecycle, performance, and reliability from the inside out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live API:&lt;/strong&gt; &lt;a href="http://my-live-domain-or-ip" rel="noopener noreferrer"&gt;http://my-live-domain-or-ip&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;GitHub Repo:&lt;/strong&gt; &lt;a href="https://github.com/Techgirli" rel="noopener noreferrer"&gt;https://github.com/Techgirli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project strengthened my skills in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux server management&lt;/li&gt;
&lt;li&gt;Reverse proxy configuration with Nginx&lt;/li&gt;
&lt;li&gt;Service reliability &amp;amp; process management&lt;/li&gt;
&lt;li&gt;API structure and performance optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On to the next stage &lt;/p&gt;

&lt;h1&gt;
  
  
  DevOps #HNGInternship #Backend #Nginx #Linux #CloudComputing #APIs
&lt;/h1&gt;

</description>
      <category>devops</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>These are some Images from the App.</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Thu, 09 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/these-are-some-images-from-the-app-4n9j</link>
      <guid>https://forem.com/techgirli/these-are-some-images-from-the-app-4n9j</guid>
      <description>&lt;p&gt;GitHub Repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Innovators-UmojaAgri-Org" rel="noopener noreferrer"&gt;
        Innovators-UmojaAgri-Org
      &lt;/a&gt; / &lt;a href="https://github.com/Innovators-UmojaAgri-Org/UmojaAgri-Org" rel="noopener noreferrer"&gt;
        UmojaAgri-Org
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;UmojaAgri-Org&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;UmojaAgri 🌾&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;AI-Driven Agricultural Logistics Platform&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;A logistics intelligence platform that helps farmers and transporters monitor produce freshness, predict delivery delays, and reduce post-harvest losses using rule-based AI
With the collaboration of the twam we could bring the mobile app to life.&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Project Overview&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;UmojaAgri is an MVP designed to address a major challenge in African agriculture: &lt;strong&gt;food spoilage due to unpredictable transportation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The platform calculates a &lt;strong&gt;Freshness Window&lt;/strong&gt; — a real-time countdown that determines whether produce will arrive at its destination before spoilage risk becomes critical.&lt;/p&gt;
&lt;p&gt;It combines logistics tracking, rule-based intelligence, and supply-chain visibility into a single system.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Problem Statement&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A significant percentage of agricultural produce in Africa spoils before reaching markets due to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor road conditions&lt;/li&gt;
&lt;li&gt;Unpredictable travel times&lt;/li&gt;
&lt;li&gt;Lack of real-time freshness tracking&lt;/li&gt;
&lt;li&gt;Inefficient logistics coordination&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UmojaAgri provides predictive insights so farmers and transporters can make smarter decisions and reduce waste.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Core Features&lt;/h1&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Produce&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Innovators-UmojaAgri-Org/UmojaAgri-Org" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Live Demo (Render):&lt;br&gt;
Farmers Dashboard: &lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org-1.onrender.com/#/dashboard" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org-1.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Marketer Dashboard: &lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org-1.onrender.com/#/home-marketer" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org-1.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Transporter Dashboard: &lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3in78l643c3dy2jf7fme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3in78l643c3dy2jf7fme.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjmww81b3epteb0j9ta0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjmww81b3epteb0j9ta0.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsb9k4rle1n796q5b1j0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsb9k4rle1n796q5b1j0.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fal91g7j3j22qtxh4mlg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fal91g7j3j22qtxh4mlg3.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1ngys4wa9swpcteas4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1ngys4wa9swpcteas4n.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mobileapp</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Slides on the information UmojaAgri</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Tue, 07 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/the-slides-on-the-information-umojaagri-3pbh</link>
      <guid>https://forem.com/techgirli/the-slides-on-the-information-umojaagri-3pbh</guid>
      <description>&lt;p&gt;If you’d like to explore UmojaAgri 🌱 in action:&lt;br&gt;
Check out more information &lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://www.canva.com/design/DAHEywhPj3E/-hDsdWXEPDaqebvMcXW9Kw/edit?utm_content=DAHEywhPj3E&amp;amp;utm_campaign=designshare&amp;amp;utm_medium=link2&amp;amp;utm_source=sharebutton" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;canva.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eco7iqw73aq13enlpxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eco7iqw73aq13enlpxw.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>You can Explore the app</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Mon, 06 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/you-can-explore-the-app-55i8</link>
      <guid>https://forem.com/techgirli/you-can-explore-the-app-55i8</guid>
      <description>&lt;p&gt;If you’d like to explore UmojaAgri 🌱 in action:&lt;br&gt;
GitHub Repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Innovators-UmojaAgri-Org" rel="noopener noreferrer"&gt;
        Innovators-UmojaAgri-Org
      &lt;/a&gt; / &lt;a href="https://github.com/Innovators-UmojaAgri-Org/UmojaAgri-Org" rel="noopener noreferrer"&gt;
        UmojaAgri-Org
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;UmojaAgri-Org&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;UmojaAgri 🌾&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;AI-Driven Agricultural Logistics Platform&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;A logistics intelligence platform that helps farmers and transporters monitor produce freshness, predict delivery delays, and reduce post-harvest losses using rule-based AI
With the collaboration of the twam we could bring the mobile app to life.&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Project Overview&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;UmojaAgri is an MVP designed to address a major challenge in African agriculture: &lt;strong&gt;food spoilage due to unpredictable transportation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The platform calculates a &lt;strong&gt;Freshness Window&lt;/strong&gt; — a real-time countdown that determines whether produce will arrive at its destination before spoilage risk becomes critical.&lt;/p&gt;
&lt;p&gt;It combines logistics tracking, rule-based intelligence, and supply-chain visibility into a single system.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Problem Statement&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A significant percentage of agricultural produce in Africa spoils before reaching markets due to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor road conditions&lt;/li&gt;
&lt;li&gt;Unpredictable travel times&lt;/li&gt;
&lt;li&gt;Lack of real-time freshness tracking&lt;/li&gt;
&lt;li&gt;Inefficient logistics coordination&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UmojaAgri provides predictive insights so farmers and transporters can make smarter decisions and reduce waste.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Core Features&lt;/h1&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Produce&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Innovators-UmojaAgri-Org/UmojaAgri-Org" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Live Demo (Render):&lt;br&gt;
Farmers Dashboard: &lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org-1.onrender.com/#/dashboard" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org-1.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Marketer Dashboard: &lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org-1.onrender.com/#/home-marketer" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org-1.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Transporter Dashboard: &lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://umojaagri-org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;umojaagri-org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Project Structure Highlights:&lt;br&gt;
/frontend → Flutter application&lt;br&gt;
/backend → Supabase integration &amp;amp; services&lt;br&gt;
/nginx → Reverse proxy configuration&lt;br&gt;
.github/workflows → CI/CD automation&lt;/p&gt;

&lt;p&gt;What this project demonstrates:&lt;br&gt;
Full-stack collaboration (Flutter + Supabase + PostgreSQL)&lt;br&gt;
DevOps pipeline design using GitHub Actions&lt;br&gt;
Cloud deployment and management via Render&lt;br&gt;
Real-world debugging and system optimization&lt;/p&gt;

&lt;p&gt;Results:&lt;br&gt;
Faster deployment cycles&lt;br&gt;
Improved reliability through automation&lt;br&gt;
Clean and scalable project structure&lt;/p&gt;

&lt;p&gt;Future Improvements:&lt;br&gt;
Container orchestration using Kubernetes&lt;br&gt;
Monitoring &amp;amp; observability with Prometheus + Grafana&lt;br&gt;
Centralized logging and alerting&lt;br&gt;
Scaling into microservices architecture&lt;br&gt;
I’m actively seeking DevOps / Cloud Engineering roles where I can contribute to building scalable, production-ready systems.&lt;br&gt;
Let’s connect &lt;br&gt;
hashtag#DevOpsEngineer hashtag#CloudEngineering hashtag#Kubernetes hashtag#Prometheus hashtag#Grafana hashtag#GitHub hashtag#Portfolio hashtag#OpenToWork&lt;br&gt;
umoja_agri&lt;br&gt;
umojaagri-org-1.onrender.com&lt;/p&gt;

</description>
      <category>mobiledev</category>
      <category>devops</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Here’s how we structured and deployed UmojaAgri 🌱 from a DevOps perspective. Architecture Overview:</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Fri, 03 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/heres-how-we-structured-and-deployed-umojaagri-from-a-devops-perspectivearchitecture-overview-51i6</link>
      <guid>https://forem.com/techgirli/heres-how-we-structured-and-deployed-umojaagri-from-a-devops-perspectivearchitecture-overview-51i6</guid>
      <description>&lt;p&gt;Here’s how we structured and deployed UmojaAgri 🌱 from a DevOps perspective.&lt;br&gt;
Architecture Overview:&lt;br&gt;
Flutter (Frontend) → API layer (Supabase) → PostgreSQL database&lt;br&gt;
Nginx acting as a reverse proxy (where applicable)&lt;br&gt;
Application services deployed via Render&lt;br&gt;
CI/CD handled with GitHub Actions&lt;/p&gt;

&lt;p&gt;Infrastructure Stack:&lt;br&gt;
Hosting: Render&lt;br&gt;
Backend Services: Supabase&lt;br&gt;
Database: PostgreSQL&lt;br&gt;
CI/CD: GitHub Actions&lt;br&gt;
Containerization: Docker&lt;br&gt;
Server Config: Nginx&lt;br&gt;
CI/CD Workflow:&lt;br&gt;
Code pushed to GitHub&lt;br&gt;
GitHub Actions triggers build &amp;amp; checks&lt;br&gt;
Updates deployed to Render&lt;br&gt;
Backend services sync via Supabase&lt;/p&gt;

&lt;p&gt;System Improvements:&lt;br&gt;
Faster and consistent deployments&lt;br&gt;
Simplified rollback via version control&lt;br&gt;
Reduced human error through automation&lt;br&gt;
DevOps Principles Applied:&lt;br&gt;
Automation-first workflows&lt;br&gt;
Service separation (frontend, backend, database)&lt;br&gt;
Scalable and maintainable architecture&lt;/p&gt;

&lt;p&gt;Next: GitHub repo + live demo + future upgrades 👇&lt;br&gt;
hashtag#DevOps hashtag#SystemDesign hashtag#Cloud hashtag#Supabase hashtag#PostgreSQL hashtag#Render hashtag#Infrastructure&lt;/p&gt;

&lt;p&gt;Back&lt;br&gt;
Dialog content end.&lt;br&gt;
Miracle OlorunsolaStatus is online&lt;br&gt;
MessagingYou are on the messaging overlay. Press enter to open the list of conversations.&lt;/p&gt;

&lt;p&gt;Compose message&lt;br&gt;
You are on the messaging overlay. Press enter to open the list of conversations.&lt;/p&gt;

</description>
      <category>mobiledev</category>
      <category>devops</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Created and Deployed an Agri-Tech</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Thu, 02 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/created-and-deployed-an-agri-tech-47la</link>
      <guid>https://forem.com/techgirli/created-and-deployed-an-agri-tech-47la</guid>
      <description>&lt;p&gt;I recently joined a team of amazing women to build a mobile app called UmojaAgri Santana Jepchumba Blen Redwan Michelle Fayeun Blessing Omonye Abanonkhua Nkechika Collins Akpe Rotimi Amusa &lt;/p&gt;

&lt;p&gt;I deployed UmojaAgri 🌱  an AgriTech platform focused on improving how farmers access tools, insights, and resources.&lt;/p&gt;

&lt;p&gt;But beyond development, I approached this project with a DevOps and production-first mindset.&lt;/p&gt;

&lt;p&gt;What I implemented:&lt;/p&gt;

&lt;p&gt;Containerization using Docker&lt;/p&gt;

&lt;p&gt;Automated CI/CD pipelines with GitHub Actions&lt;/p&gt;

&lt;p&gt;Reverse proxy configuration using Nginx&lt;/p&gt;

&lt;p&gt;Cloud deployment using Render&lt;/p&gt;

&lt;p&gt;Debugging real-world deployment issues (including “Cannot GET /”)&lt;/p&gt;

&lt;p&gt;Performance &amp;amp; Impact:&lt;/p&gt;

&lt;p&gt;Deployment time reduced by ~60% using CI/CD automation&lt;/p&gt;

&lt;p&gt;Average response time improved through optimized server routing&lt;/p&gt;

&lt;p&gt;Achieved stable uptime during testing and deployment phases&lt;/p&gt;

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

&lt;p&gt;This wasn’t just about building features  it was about shipping reliable, scalable systems.&lt;/p&gt;

&lt;p&gt;I’ve included an architecture breakdown in the next post &lt;/p&gt;

&lt;h1&gt;
  
  
  DevOps #CloudEngineering #Render #Docker #CI_CD #Nginx #AgriTech #OpenToWork
&lt;/h1&gt;

</description>
      <category>mobiledev</category>
      <category>devops</category>
    </item>
    <item>
      <title>FAILURE ANALYSIS &amp; DEBUGGING</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Sun, 01 Mar 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/failure-analysis-debugging-29p6</link>
      <guid>https://forem.com/techgirli/failure-analysis-debugging-29p6</guid>
      <description>&lt;p&gt;What Pipeline Failures Taught Me&lt;/p&gt;

&lt;p&gt;Failures included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Missing tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Incorrect build paths&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credential scope issues&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each failure improved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pipeline resilience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Observability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;System understanding&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DevSecOps is about controlled failure.&lt;br&gt;
This project demonstrates hands-on experience with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Jenkins CI/CD&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker &amp;amp; Kubernetes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Terraform &amp;amp; AWS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Container security&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secure infrastructure design&lt;/p&gt;

&lt;p&gt;I’m actively growing as a DevSecOps / Cloud Engineer and open to opportunities.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>Secure Secrets Injection in Terraform &amp; Jenkins</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Sat, 28 Feb 2026 12:00:00 +0000</pubDate>
      <link>https://forem.com/techgirli/secure-secrets-injection-in-terraform-jenkins-3c8n</link>
      <guid>https://forem.com/techgirli/secure-secrets-injection-in-terraform-jenkins-3c8n</guid>
      <description>&lt;p&gt;Secrets are injected at runtime using:&lt;/p&gt;

&lt;p&gt;Jenkins credentials&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;TF_VAR_db_username&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TF_VAR_db_password&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Not committed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not logged&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not baked into images&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This limits blast radius.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>jenkins</category>
      <category>security</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Provisioning AWS Infrastructure with Terraform</title>
      <dc:creator>Miracle Olorunsola</dc:creator>
      <pubDate>Wed, 25 Feb 2026 12:30:00 +0000</pubDate>
      <link>https://forem.com/techgirli/provisioning-aws-infrastructure-with-terraform-a1c</link>
      <guid>https://forem.com/techgirli/provisioning-aws-infrastructure-with-terraform-a1c</guid>
      <description>&lt;p&gt;Infrastructure is code — and should be treated that way.&lt;/p&gt;

&lt;p&gt;Terraform manages:&lt;/p&gt;

&lt;p&gt;VPC networking&lt;/p&gt;

&lt;p&gt;EKS cluster&lt;/p&gt;

&lt;p&gt;IAM permissions&lt;/p&gt;

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

&lt;p&gt;Version control&lt;/p&gt;

&lt;p&gt;Repeatability&lt;/p&gt;

&lt;p&gt;Reduced misconfiguration risk&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>cicd</category>
      <category>terraform</category>
    </item>
  </channel>
</rss>
