<?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: Faizan Firdousi</title>
    <description>The latest articles on DEV Community by Faizan Firdousi (@faizanfirdousi).</description>
    <link>https://dev.to/faizanfirdousi</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%2F2284696%2F89648f6a-e34b-4a8f-b5f3-94e6005ecce5.jpeg</url>
      <title>DEV Community: Faizan Firdousi</title>
      <link>https://dev.to/faizanfirdousi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faizanfirdousi"/>
    <language>en</language>
    <item>
      <title>Build a Container from Scratch in Go (Modern Namespaces + cgroup v2)</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Wed, 18 Feb 2026 15:38:24 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/build-a-container-from-scratch-in-go-modern-namespaces-cgroup-v2-5556</link>
      <guid>https://dev.to/faizanfirdousi/build-a-container-from-scratch-in-go-modern-namespaces-cgroup-v2-5556</guid>
      <description>&lt;p&gt;So I made a container from scratch in Go with the minimum number of lines of code possible and learned various things that usually happen inside a container and that is what Docker abstracts away.&lt;/p&gt;

&lt;p&gt;Although you may get other articles about the same, I wrote this because of some changes in the Linux kernel, the blogs got somewhat deprecated in terms of understanding and implementation of code.&lt;/p&gt;

&lt;h1&gt;
  
  
  What are containers?
&lt;/h1&gt;

&lt;p&gt;Let's understand what containers are. As you already might know, fundamentally, containers are "about bundling up dependencies so we can ship code around in a repeatable, secure way."&lt;/p&gt;

&lt;p&gt;But let's understand the underlying concepts - Namespaces, Cgroups, and Filesystem isolation that make containers what they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Namespaces
&lt;/h2&gt;

&lt;p&gt;Linux namespaces are a fundamental kernel feature that provides resource isolation so different sets of processes see different sets of resources.&lt;/p&gt;

&lt;p&gt;Namespaces are very important, as we will see further how much we are going to use them because they're the core technologies containers are built on. So whenever you create containers with Docker or Podman, it automatically creates namespaces on your behalf.&lt;/p&gt;

&lt;p&gt;Namespaces primarily are of 6 types (note that we will cover them more while coding the container, as that is how I like learning by making things so I'm not bombarding you with a lot of information firsthand):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PID&lt;/strong&gt; - Assigns independent process IDs. The first process in a new namespace gets PID 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; - Provides an independent network stack via virtual ethernet pairs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MNT&lt;/strong&gt; - Maintains an independent list of mount points, allowing filesystem mounts/unmounts without affecting the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USER&lt;/strong&gt; - Has its own user IDs and group IDs, allowing a process to have root privilege within its namespace without having it elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IPC&lt;/strong&gt; - Isolates interprocess communication resources like POSIX message queues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UTS&lt;/strong&gt; - Allows different host and domain names for different processes on the same system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cgroups
&lt;/h2&gt;

&lt;p&gt;While understanding cgroups and knowing on the surface level what cgroups do is very easy it basically means control groups, it's a Linux kernel feature that limits, accounts for, and isolates the resource usage including CPU, memory, disk I/O, and network of collections of processes.&lt;/p&gt;

&lt;p&gt;We can talk a lot about cgroups, but I'll stick to the basics required for implementation, and don't worry, I'll talk about it more again down further.&lt;/p&gt;

&lt;p&gt;Let's name the phases of code as it will be beneficial to go one by one, understanding deeply.&lt;/p&gt;

&lt;p&gt;here's the github link for full code(please give a star)- &lt;br&gt;
&lt;a href="https://github.com/faizanfirdousi/container-from-scratch" rel="noopener noreferrer"&gt;https://github.com/faizanfirdousi/container-from-scratch&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Phase 0
&lt;/h1&gt;

&lt;p&gt;First, let me clarify what we're doing here exactly our main goal is to create a container that is isolated from the host machine. In other words, we're building a sandboxed environment that runs a shell with its own filesystem, process namespace, and resource limits, giving it strong isolation from the host.&lt;/p&gt;

&lt;p&gt;The container can be of anything. Here, for simplicity, we are going to have an Alpine Linux container as it's smaller in size and also has full components that give the feel of a whole system. At the end, we will create a container or basically a process that thinks the Alpine directory is the entire computer.&lt;/p&gt;

&lt;p&gt;For that, we need to have the root filesystem of Alpine, so set this up before starting to code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download and extract Alpine Linux rootfs&lt;/span&gt;
docker &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker create alpine&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; alpine.tar
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/faizan/alpine-rootfs
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; alpine.tar &lt;span class="nt"&gt;-C&lt;/span&gt; /home/faizan/alpine-rootfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't have Docker, then install it via curl. I just used it for simplicity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Phase 1
&lt;/h1&gt;

&lt;p&gt;First, we're going to create a simple program that runs commands*&lt;em&gt;but it won't have any isolation yet&lt;/em&gt;*. This might seem counterintuitive, but there's a good reason: by seeing what happens without isolation, you'll truly understand what each isolation primitive does when we add it later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/exec"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Usage: %s run &amp;lt;cmd&amp;gt; [args...]&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown command"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&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;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What this code does
&lt;/h2&gt;

&lt;p&gt;The program starts by checking the arguments passed to it. The first argument must be &lt;code&gt;run&lt;/code&gt;, which tells our program that we want to execute a command. Everything after &lt;code&gt;run&lt;/code&gt; becomes the actual command and its arguments that we want to execute for example, &lt;code&gt;/bin/bash&lt;/code&gt; or &lt;code&gt;/bin/sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;run&lt;/code&gt; function is called, it uses Go's &lt;code&gt;exec.Command&lt;/code&gt; to create a new process. The way this works is straightforward: &lt;code&gt;os.Args[2]&lt;/code&gt; contains the command itself (like &lt;code&gt;/bin/bash&lt;/code&gt;), and &lt;code&gt;os.Args[3:]&lt;/code&gt; contains any additional arguments you want to pass to that command. The &lt;code&gt;...&lt;/code&gt; syntax spreads those arguments out so they're passed individually to the command.&lt;/p&gt;

&lt;p&gt;We then connect the standard input, output, and error streams. This is important because it allows the command to interact with your terminal naturally. You can type input and see output just like you would with any normal command. Finally, &lt;code&gt;cmd.Run()&lt;/code&gt; actually executes the command and waits for it to complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go run /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash]
&lt;span class="o"&gt;[&lt;/span&gt;root@archlinux cfs]#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test if it's isolated by running commands like &lt;code&gt;ps&lt;/code&gt; and &lt;code&gt;hostname&lt;/code&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%2Fwqx72lsjwriw8i1oq4wa.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%2Fwqx72lsjwriw8i1oq4wa.png" alt=" " width="508" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let me show you what happens when I test this. Run &lt;code&gt;ps&lt;/code&gt; inside the shell and look closely at the output.&lt;/p&gt;

&lt;p&gt;Notice something interesting? The PIDs are not starting from 1 like they would in a real container. Instead, you're seeing PIDs like 46628, 46629, 46669 ,these are the exact same process IDs that the host system sees. If you open another terminal window on your host and run &lt;code&gt;ps aux&lt;/code&gt; there, you'll see the exact same PID values. This proves we're sharing the same process namespace as the host.&lt;/p&gt;

&lt;p&gt;Now let's check the hostname.&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%2Fsqa5gwhv12li5y81895z.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%2Fsqa5gwhv12li5y81895z.png" alt=" " width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;hostname&lt;/code&gt; and you'll see your machine's actual hostname. Try changing it with &lt;code&gt;sudo hostname test-container&lt;/code&gt;, then run &lt;code&gt;hostname&lt;/code&gt; again to confirm it changed. Exit the shell and check your host's hostname, it's been changed there too! We just modified the host system's hostname directly. There's no isolation barrier protecting the host from changes made inside our "container."&lt;/p&gt;

&lt;p&gt;The same goes for the filesystem. When you run &lt;code&gt;ls /&lt;/code&gt;, you're looking at your actual host's root directory. Any file you create in &lt;code&gt;/tmp&lt;/code&gt; will appear on the host. We're operating directly on the host system with zero separation.&lt;/p&gt;

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

&lt;p&gt;What we've built right now is basically just a wrapper that executes commands, there's no containerization happening at all. But this baseline is important. In the next steps, when we add Linux namespaces, you'll see how dramatically things change. The PIDs will start from 1, hostname changes won't affect the host, and we'll have our own isolated filesystem.&lt;/p&gt;

&lt;h1&gt;
  
  
  Phase 2
&lt;/h1&gt;

&lt;p&gt;Now update the &lt;code&gt;run()&lt;/code&gt; function to look exactly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;// Re-exec ourselves as "child" inside new namespaces&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/proc/self/exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&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="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWPID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Unshareflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add this new &lt;code&gt;child()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v as child&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&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;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sethostname&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"container"&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;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Process exited:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's understand what we're doing here. First, we need to create namespaces. Before that, we write this in &lt;code&gt;run()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/proc/self/exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&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="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is the critical insight&lt;/strong&gt;: We're &lt;strong&gt;not&lt;/strong&gt; running &lt;code&gt;/bin/bash&lt;/code&gt; directly. Instead, we're re-executing &lt;strong&gt;our own Go program&lt;/strong&gt; (&lt;code&gt;/proc/self/exe&lt;/code&gt; is the path to our currently running binary) but with a new argument list: &lt;code&gt;["child", "/bin/bash"]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why this weird self-re-execution? Because &lt;strong&gt;Linux namespaces can only be created when spawning a NEW process&lt;/strong&gt;, you cannot create namespaces for a process that's already running. So we need our code to run again, but this time &lt;strong&gt;inside fresh, isolated namespaces&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then we actually create the namespaces using this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWPID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Unshareflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I told you at the start of the blog, different namespaces isolate different things. What we're doing here is creating namespaces using &lt;strong&gt;Linux system calls&lt;/strong&gt;. Specifically, we're using the &lt;code&gt;clone()&lt;/code&gt; system call with special flags.&lt;/p&gt;

&lt;p&gt;The system calls we're using are the &lt;code&gt;CLONE_*&lt;/code&gt; family. Each &lt;code&gt;CLONE_NEW*&lt;/code&gt; flag tells the Linux kernel: "When you create this child process, put it in a completely separate namespace for this resource." Let me break down what each one does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CLONE_NEWUTS&lt;/code&gt;&lt;/strong&gt; creates a new UTS (Unix Time-Sharing) namespace. This isolates the hostname and domain name. With this flag, when the child process changes its hostname to "container", the host machine's hostname stays completely unchanged. Without this, changing the hostname inside would affect your actual host system exactly what we saw in Phase 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CLONE_NEWPID&lt;/code&gt;&lt;/strong&gt; creates a new PID (Process ID) namespace. This is huge. It means the child process gets its own, completely separate process ID numbering system. Inside the container, the shell will see itself as PID 1—the first process, just like the init system in a real Linux boot. But from the host's perspective, that same process might be PID 15234. This is why when you run &lt;code&gt;ps aux&lt;/code&gt; in a real container, you only see the container's processes, not all the host processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CLONE_NEWNS&lt;/code&gt;&lt;/strong&gt; creates a new Mount namespace. This isolates filesystem mount points. Any mounts we do inside the container (like mounting &lt;code&gt;/proc&lt;/code&gt; or &lt;code&gt;/tmp&lt;/code&gt;) won't appear on the host system, and vice versa. This is crucial for filesystem isolation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;|&lt;/code&gt; (pipe) symbol between these flags is a bitwise OR operation—it's how we enable multiple namespaces at once. We're essentially saying "create ALL THREE of these namespaces for the child process."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Unshareflags&lt;/code&gt;&lt;/strong&gt; with &lt;code&gt;CLONE_NEWNS&lt;/code&gt; is an additional safety measure. It makes the mount namespace "unshareable" to prevent mount propagation, basically ensuring that any filesystem changes inside the container absolutely cannot leak to the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;Now let's test if our namespace isolation is actually working. Run your updated code:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash]
Running &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash] as child
&lt;span class="o"&gt;[&lt;/span&gt;root@container cfs]#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice that the hostname in your prompt already shows &lt;code&gt;container&lt;/code&gt;—that's the &lt;code&gt;Sethostname&lt;/code&gt; call working.&lt;/p&gt;

&lt;p&gt;Now check if hostname changes stay isolated by changing the hostname of the container and checking if the host's hostname also changed.&lt;/p&gt;

&lt;p&gt;Now check the PID namespace isolation - this is interesting and important.&lt;/p&gt;

&lt;p&gt;Inside the container, run:&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;echo&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On your host (different terminal):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;Same process, two different PIDs!&lt;/strong&gt; Inside the namespace it's PID 7 (can be different but a low number for you), from the host it's PID 44999. This proves PID isolation is working.&lt;/p&gt;

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

&lt;p&gt;Run &lt;code&gt;ps&lt;/code&gt; inside the container:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll see &lt;strong&gt;all the host's processes&lt;/strong&gt;—systemd, kthreadd, Chrome, everything. Why? Because even though we have PID namespace isolation, we're still reading the &lt;strong&gt;host's&lt;/strong&gt; &lt;code&gt;/proc&lt;/code&gt; filesystem. The &lt;code&gt;ps&lt;/code&gt; command gets its information from &lt;code&gt;/proc&lt;/code&gt;, which we haven't isolated yet.&lt;/p&gt;

&lt;h1&gt;
  
  
  Phase 3
&lt;/h1&gt;

&lt;p&gt;Update your &lt;code&gt;child()&lt;/code&gt; function to add filesystem isolation. First, add environment variables right after the &lt;code&gt;cmd&lt;/code&gt; setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&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;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="c"&gt;// Add these environment variables&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"HOME=/root"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"TERM=xterm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sethostname&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;// Add filesystem isolation&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/home/faizan/alpine-rootfs"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Mount /proc filesystem&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Mount tmpfs at /tmp&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MkdirAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tmpfs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tmpfs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&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;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Process exited:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Cleanup mounts on exit&lt;/span&gt;
    &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"HOME=/root"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"TERM=xterm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small but important detail. When a child process is spawned, by default it inherits the parent's environment variables, which means it would inherit your host's &lt;code&gt;PATH&lt;/code&gt;, your host's &lt;code&gt;HOME&lt;/code&gt;, all of it. That's a problem because after we do the filesystem isolation in the next step, those host paths won't even exist inside our container.&lt;/p&gt;

&lt;p&gt;So we're explicitly setting a clean, minimal environment. &lt;code&gt;PATH&lt;/code&gt; tells the shell where to look for binaries. &lt;code&gt;HOME&lt;/code&gt; sets the home directory. &lt;code&gt;TERM=xterm&lt;/code&gt; makes sure terminal behavior works correctly, things like clearing the screen, arrow keys, color output. Without &lt;code&gt;TERM&lt;/code&gt;, a lot of terminal programs behave weirdly or refuse to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chrooting (the important step)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/home/faizan/alpine-rootfs"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chroot&lt;/code&gt; redefines what &lt;code&gt;/&lt;/code&gt; means for this process and all its children. After this call, when the shell opens &lt;code&gt;/etc/passwd&lt;/code&gt;, the kernel resolves it as &lt;code&gt;/home/faizan/alpine-rootfs/etc/passwd&lt;/code&gt;. The host filesystem becomes completely invisible. Run &lt;code&gt;ls /&lt;/code&gt; and you see Alpine's &lt;code&gt;bin&lt;/code&gt;, &lt;code&gt;etc&lt;/code&gt;, &lt;code&gt;usr&lt;/code&gt;, exactly like a real Alpine machine.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chroot&lt;/code&gt; changes where &lt;code&gt;/&lt;/code&gt; points, but your current working directory doesn't move. So in the directory you are running the code from, you're still sitting there after the &lt;code&gt;chroot&lt;/code&gt;, that means outside the jail. From there, a process can just do &lt;code&gt;cd ../../..&lt;/code&gt; and walk right out. &lt;code&gt;os.Chdir("/")&lt;/code&gt; moves you inside the jail immediately, so there's nowhere to escape to, which is perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mounting &lt;code&gt;/proc&lt;/code&gt; - Fixing What &lt;code&gt;ps&lt;/code&gt; Reads
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;chroot&lt;/code&gt;, Alpine's &lt;code&gt;/proc&lt;/code&gt; is an empty directory. The &lt;code&gt;ps&lt;/code&gt; command reads everything from &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/status&lt;/code&gt; without mounting something there, &lt;code&gt;ps&lt;/code&gt; would just fail.&lt;/p&gt;

&lt;p&gt;So we mount a fresh &lt;code&gt;procfs&lt;/code&gt;. The key detail: this is not a copy of the host's &lt;code&gt;/proc&lt;/code&gt;. The kernel populates it based on &lt;strong&gt;what the current PID namespace can see&lt;/strong&gt;. Since we're inside &lt;code&gt;CLONE_NEWPID&lt;/code&gt; from Phase 2, only the container's processes appear. Run &lt;code&gt;ps aux&lt;/code&gt; now and you'll only see the container's shell. The host process flood is gone. This is PID isolation and filesystem isolation finally working together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mounting &lt;code&gt;tmpfs&lt;/code&gt; at &lt;code&gt;/tmp&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MkdirAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tmpfs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tmp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tmpfs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tmpfs&lt;/code&gt; is memory-backed. Anything written to &lt;code&gt;/tmp&lt;/code&gt; inside the container lives in RAM, never touches your host's &lt;code&gt;/tmp&lt;/code&gt;, and disappears the moment the container exits. This is also why &lt;code&gt;CLONE_NEWNS&lt;/code&gt; from Phase 2 mattered. Without the mount namespace, these mounts would propagate to the host's mount table. The mount namespace is the prerequisite; these mounts are the payload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;now check everything if it's showing file system and process of the container and not of the host&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%2F4v5e08d8e2am1ivhu92q.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%2F4v5e08d8e2am1ivhu92q.png" alt=" " width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Phase 4 - Cgroups
&lt;/h1&gt;

&lt;p&gt;We now have a container with real isolation, its own filesystem, its own process tree, its own hostname. But there's still one problem: nothing stops a process inside this container from consuming all of your host's CPU, spawning thousands of processes, or eating all your RAM. A simple fork bomb inside the container would take down your entire machine.&lt;/p&gt;

&lt;p&gt;This is what &lt;strong&gt;cgroups&lt;/strong&gt; (control groups) solve. While namespaces control what a process can &lt;em&gt;see&lt;/em&gt;, cgroups control what it can &lt;em&gt;use&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So update your code with this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;cg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cgroupRoot&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"/sys/fs/cgroup/"&lt;/span&gt;
    &lt;span class="n"&gt;cgroupName&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"container-cgroup"&lt;/span&gt;
    &lt;span class="n"&gt;containerCgroup&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cgroupRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cgroupName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cgroupRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cgroup.subtree_control"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"+pids +memory +cpu"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MkdirAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pids.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"memory.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"52428800"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cpu.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"50000 100000"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cgroup.procs"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
        &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cgroup v2
&lt;/h2&gt;

&lt;p&gt;This is what I wrote this blog mainly for. You see, most of the blogs were based on cgroups v1, but it got changed to cgroup v2 a long time ago, so eventually the code's gotta change as well.&lt;/p&gt;

&lt;p&gt;Cgroup v2 uses a &lt;strong&gt;unified hierarchy&lt;/strong&gt;. Every controller—pids, memory, cpu, lives under a single tree at &lt;code&gt;/sys/fs/cgroup/&lt;/code&gt;. This is different from v1, which had separate trees per controller, like &lt;code&gt;/sys/fs/cgroup/pids/&lt;/code&gt;, &lt;code&gt;/sys/fs/cgroup/memory/&lt;/code&gt;, and so on. If you've seen old container blogs using v1 paths, that's why they're broken on modern kernels.&lt;/p&gt;

&lt;p&gt;Working with cgroups is entirely done through the filesystem—you create directories, write to files. No special syscalls needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cgroupRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cgroup.subtree_control"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"+pids +memory +cpu"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we enable the controllers on the root cgroup. Writing &lt;code&gt;+pids +memory +cpu&lt;/code&gt; to &lt;code&gt;subtree_control&lt;/code&gt; tells the kernel to make these controllers available to child cgroups we create underneath. It's like unlocking the feature before using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Cgroup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MkdirAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the entire cgroup creation. You make a directory under &lt;code&gt;/sys/fs/cgroup/&lt;/code&gt; and the kernel sees it, recognizes it as a new cgroup, and automatically populates it with control files, &lt;code&gt;pids.max&lt;/code&gt;, &lt;code&gt;memory.max&lt;/code&gt;, &lt;code&gt;cpu.max&lt;/code&gt;, &lt;code&gt;cgroup.procs&lt;/code&gt;, and more. Nothing else needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Limits
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pids.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"memory.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"52428800"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cpu.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"50000 100000"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;pids.max&lt;/code&gt;&lt;/strong&gt; is the most immediately important one. Setting it to &lt;code&gt;20&lt;/code&gt; means the kernel will start rejecting &lt;code&gt;fork()&lt;/code&gt; and &lt;code&gt;clone()&lt;/code&gt; calls the moment the process count hits 20. A fork bomb inside the container just chokes and dies, the host is untouched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;memory.max&lt;/code&gt;&lt;/strong&gt; is &lt;code&gt;52428800&lt;/code&gt; bytes - 50 MiB. If the container crosses this, the kernel's OOM killer steps in and kills the process that pushed it over. The host's memory is never at risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cpu.max&lt;/code&gt;&lt;/strong&gt; format is &lt;code&gt;$MAX $PERIOD&lt;/code&gt; in microseconds. &lt;code&gt;50000 100000&lt;/code&gt; means: out of every 100ms window, this cgroup gets at most 50ms of CPU time, 50% of one core. A runaway CPU loop inside the container gets throttled at the kernel scheduler level. Your machine stays responsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving the Process In
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cgroup.procs"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
    &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything before this was just configuration. This line is what actually enforces it. Writing our PID to &lt;code&gt;cgroup.procs&lt;/code&gt; moves the current process into the cgroup. From this point, this process and every child it spawns—including the shell and everything the user runs inside, is subject to all three limits above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;p&gt;Try a fork bomb inside the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;:&lt;span class="o"&gt;(){&lt;/span&gt; :|:&amp;amp; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;pids.max = 20&lt;/code&gt;, the kernel rejects every &lt;code&gt;fork()&lt;/code&gt; past the limit. The container shell dies. Open another terminal on your host, everything is running perfectly. Without cgroups, that fork bomb would have required a hard reboot.&lt;/p&gt;




&lt;p&gt;That's it! You've built a real container from scratch with proper isolation using namespaces, filesystem virtualization with chroot, and resource limits with cgroups v2. This is fundamentally what Docker does under the hood, just with a lot more features, better UX, and production-grade tooling on top.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>go</category>
      <category>containers</category>
      <category>linux</category>
    </item>
    <item>
      <title>Why Nothing Really Gets Deleted Immediately: How Database Deletion Works Through Tombstones, LSM-Trees, and Compaction</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Tue, 10 Feb 2026 19:58:53 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/why-nothing-really-gets-deleted-immediately-how-database-deletion-works-through-tombstones-2n8m</link>
      <guid>https://dev.to/faizanfirdousi/why-nothing-really-gets-deleted-immediately-how-database-deletion-works-through-tombstones-2n8m</guid>
      <description>&lt;p&gt;Do you know that when you delete something, it doesn't actually get deleted and erased immediately? Like, you press the delete button and expect the database to be like "ok boss, I'll remove the existence of this thing" ..... but no, it doesn't happen like that.&lt;/p&gt;

&lt;p&gt;When we talk about modern high-performance databases, NoSQL and NewSQL systems based on LSM-trees use &lt;strong&gt;append-only data structures&lt;/strong&gt;. That means you can't just pick a random value and update or delete it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Append-Only?
&lt;/h3&gt;

&lt;p&gt;Because databases are highly optimized and they found out that they need to be performant for sequential writes, which are orders of magnitude faster than random writes on disk.&lt;/p&gt;

&lt;p&gt;We all know that traditional hard disks are mechanical and have seek times. If you were to specify the particular location where to read/write the data, it becomes expensive at scale while SSDs do not suffer from mechanical seek time, random writes are still more expensive than sequential writes, just for a different reason which is Write amplification.&lt;br&gt;
That's why databases are optimized for sequential writes and not random writes.&lt;/p&gt;

&lt;p&gt;Append-only structures also provide immutability benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No in-place updates means simpler concurrency control (no locks needed for reads)&lt;/li&gt;
&lt;li&gt;Easier crash recovery&lt;/li&gt;
&lt;li&gt;Natural support for versioning&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Data is Stored
&lt;/h3&gt;

&lt;p&gt;Let's understand the working of the data structures in LSM-based databases first. When you write data, it goes to two places at the same time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memtable&lt;/strong&gt; - A sorted data structure (usually a skip list or red-black tree) that holds recent writes in memory for fast access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write Ahead Logging (WAL)&lt;/strong&gt; - An append-only disk file that records every operation sequentially before applying it.&lt;/p&gt;

&lt;p&gt;WAL is very important in making the database reliable because if the system crashes before the memtable is flushed to disk, you can replay the WAL to recover all committed operations.&lt;/p&gt;

&lt;p&gt;When the memtable gets bigger than some threshold (typically a few megabytes), it then writes it out to disk as an &lt;strong&gt;SSTable&lt;/strong&gt; (Sorted String Tables - immutable sorted key-value files stored on disk) file. This is efficient because the data is already sorted by keys in the memtable, making it easier to maintain that order in SSTables as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Delete Operations Actually Work
&lt;/h3&gt;

&lt;p&gt;So, we use &lt;strong&gt;tombstones&lt;/strong&gt;. These are one of the most elegant yet complex solutions in append-only databases.&lt;/p&gt;

&lt;p&gt;A tombstone is a special write operation that marks data as deleted without removing it. When you execute a &lt;code&gt;DELETE&lt;/code&gt; statement, the database treats it as an insert, not a removal. The tombstone contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The key being deleted&lt;/li&gt;
&lt;li&gt;A timestamp&lt;/li&gt;
&lt;li&gt;A deletion marker flag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you delete a key, the tombstone follows the exact same path as a regular insert: written to Memtable → appended to WAL → flushed to SSTable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So the actual deletion didn't happen yet.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Compaction: Where Deletion Really Happens
&lt;/h3&gt;

&lt;p&gt;Compaction is the exact step where deletion happens. If you don't already know, compaction happens regularly after some time, where the segments are compacted to remove the old data and keep the most recent ones.&lt;/p&gt;

&lt;p&gt;Tombstones are physically removed during compaction, but only when specific conditions are met:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grace period must expire&lt;/strong&gt; :- The tombstone must be older than the configured grace period (&lt;code&gt;gc_grace_seconds&lt;/code&gt; in Cassandra or similar TTL-based settings in other systems)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;All relevant SSTables must participate&lt;/strong&gt; :- If SSTable-A has the tombstone but SSTable-B has older data for the same key, both must be compacted together&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After this is done, the disk space is reclaimed again.&lt;/p&gt;

&lt;p&gt;There are more real-world challenges and stuff like multi-level compaction strategies, but that's a topic for another deep dive.&lt;/p&gt;

&lt;p&gt;If you liked the blog do follow me and read other of my blogs&lt;/p&gt;

</description>
      <category>database</category>
      <category>softwareengineering</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Understanding Database Models: DDIA Chapter 2</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Thu, 15 Jan 2026 16:01:34 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/understanding-database-models-ddia-chapter-2-36jj</link>
      <guid>https://dev.to/faizanfirdousi/understanding-database-models-ddia-chapter-2-36jj</guid>
      <description>&lt;p&gt;This chapter from &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt; explores different database models and strategies for structuring application data. Understanding these concepts is crucial for making informed architectural decisions in modern software development.&lt;/p&gt;

&lt;p&gt;by the way,&lt;a href="https://dev.to/faizanfirdousi/my-key-takeaways-from-ddia-chapter-1-reliability-scalability-and-maintainability-3l49"&gt;Read chapter 1 here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Applications Store Data
&lt;/h2&gt;

&lt;p&gt;From an application developer's perspective, the data flow works like this: collect real-world data, model it using data structures and APIs, then store it in a database. The database might use JSON/XML documents, relational tables, or graph models, all ultimately stored as bytes on disk.&lt;/p&gt;

&lt;p&gt;While we'll explore various data models, the &lt;strong&gt;relational model&lt;/strong&gt; has proven most resilient and remains widely used. Modern applications often combine relational databases with non-relational databases (polyglot persistence) to meet specific requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  NoSQL vs SQL: Understanding the Trade-offs
&lt;/h2&gt;

&lt;p&gt;SQL databases are familiar to most developers, but NoSQL offers compelling advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher scalability and write throughput for large datasets&lt;/li&gt;
&lt;li&gt;More open-source options available&lt;/li&gt;
&lt;li&gt;Support for queries not possible in relational databases&lt;/li&gt;
&lt;li&gt;Less restrictive schemas&lt;/li&gt;
&lt;li&gt;Better performance for certain use cases, matching application data structures more closely&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Object-Relational Impedance Mismatch
&lt;/h2&gt;

&lt;p&gt;Most application code uses object-oriented programming, representing data as objects. However, relational databases store data in tables, rows, and columns. This creates an awkward translation layer between code and database the &lt;strong&gt;impedance mismatch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Object-Relational Mappers (ORMs) help by eliminating manual SQL queries, but they mostly provide abstraction rather than truly solving the underlying problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Document Models Excel
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;document model&lt;/strong&gt; is a NoSQL approach that stores data as flexible, semi-structured documents (JSON/XML) rather than rigid rows and columns. MongoDB is a popular example.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-to-Many Relationships
&lt;/h3&gt;

&lt;p&gt;Consider a LinkedIn profile where one user has multiple job positions. This one-to-many relationship was challenging for early SQL databases not designed to store multiple values per row. People created separate tables for positions, though modern SQL databases now support multi-valued fields.&lt;/p&gt;

&lt;p&gt;Document models handle this elegantly because &lt;strong&gt;JSON provides better locality&lt;/strong&gt; than multi-table schemas. Instead of multiple queries across tables, you retrieve all relevant data in a single query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many-to-One Relationships and Normalization
&lt;/h3&gt;

&lt;p&gt;When storing user inputs like city, organization, or college, &lt;strong&gt;use ID references instead of raw strings&lt;/strong&gt; (e.g., &lt;code&gt;org_id = 5&lt;/code&gt; instead of &lt;code&gt;org = "Microsoft"&lt;/code&gt;).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Models many-to-one relationships naturally (many users, one organization)&lt;/li&gt;
&lt;li&gt;Enables clean data validation through dropdowns&lt;/li&gt;
&lt;li&gt;Eliminates ambiguity from duplicate names&lt;/li&gt;
&lt;li&gt;Improves maintainability update once, reflect everywhere&lt;/li&gt;
&lt;li&gt;Allows centralized changes without touching thousands of records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For deeper exploration of normalization and schema design:&lt;br&gt;
&lt;a href="https://dev.to/faizanfirdousi/database-design-best-practice-store-categorical-data-as-ids-not-strings-4cfl"&gt;Read my detailed blog on database normalization&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The JOIN Challenge
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Joins&lt;/strong&gt; combine fields from different tables in relational databases. While straightforward in SQL, document models have weak or nonexistent join support. Without database-level joins, you must emulate them in application code creating performance overhead and shifting complexity from the storage layer to your application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Evolution of Database Models
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Hierarchical Model
&lt;/h3&gt;

&lt;p&gt;The first database model, used in IBM's IMS, employed a tree structure with single parents ideal for one-to-many relationships.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;No many-to-many relationship support&lt;/li&gt;
&lt;li&gt;Difficult denormalization decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Network Model
&lt;/h3&gt;

&lt;p&gt;Created to solve hierarchical limitations, the network model allowed multiple parents, supporting many-to-one and many-to-many relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fatal flaw:&lt;/strong&gt; Records connected like programming pointers through "access paths." These access paths made querying and updating extremely complex, negating the benefit of multiple relationship support.&lt;/p&gt;
&lt;h3&gt;
  
  
  Relational Model
&lt;/h3&gt;

&lt;p&gt;The relational model succeeded because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores data in simple tables (rows and columns)&lt;/li&gt;
&lt;li&gt;Eliminates complicated access paths&lt;/li&gt;
&lt;li&gt;Supports multiple relationships elegantly&lt;/li&gt;
&lt;li&gt;Allows inserting rows without foreign key concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The game-changer: &lt;strong&gt;query optimizers&lt;/strong&gt; that make writing efficient queries straightforward.&lt;/p&gt;
&lt;h2&gt;
  
  
  Imperative vs Declarative Queries
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Imperative code&lt;/strong&gt; tells the computer every step: loop through lists, check conditions, push to arrays like giving turn-by-turn directions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative queries&lt;/strong&gt; (like SQL) specify &lt;em&gt;what&lt;/em&gt; you want, not &lt;em&gt;how&lt;/em&gt; to get it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;animals&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;family&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Sharks'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database determines optimal execution. This is powerful because query optimizers improve performance behind the scenes without code changes.&lt;/p&gt;

&lt;p&gt;The surprising benefit: declarative languages are naturally parallelizable. Since you don't dictate execution order, databases can freely split work across multiple cores.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Relational databases remain dominant but work well alongside NoSQL for specific use cases&lt;/li&gt;
&lt;li&gt;Document models excel at one-to-many relationships with better data locality&lt;/li&gt;
&lt;li&gt;Normalization using IDs prevents data duplication and eases maintenance&lt;/li&gt;
&lt;li&gt;Declarative queries enable database-level optimization and parallelization&lt;/li&gt;
&lt;li&gt;Understanding historical models (hierarchical, network) helps appreciate modern solutions&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>distributedsystems</category>
      <category>database</category>
    </item>
    <item>
      <title>Database Design Best Practice: Store Categorical Data as IDs, Not Strings</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Sun, 11 Jan 2026 15:21:20 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/database-design-best-practice-store-categorical-data-as-ids-not-strings-4cfl</link>
      <guid>https://dev.to/faizanfirdousi/database-design-best-practice-store-categorical-data-as-ids-not-strings-4cfl</guid>
      <description>&lt;p&gt;When designing your database schema for applications that collect user information like cities, organizations, or college names, implementing a relational database structure with foreign keys is crucial for scalability and maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Storing Raw String Data
&lt;/h2&gt;

&lt;p&gt;Instead of storing &lt;code&gt;org = "microsoft"&lt;/code&gt; directly in your users table, store a reference: &lt;code&gt;org_id = 5&lt;/code&gt;. This database normalization technique offers multiple advantages for production applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of ID-Based References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improved User Experience
&lt;/h3&gt;

&lt;p&gt;Implement dropdown menus with predefined options for categorical data like cities and organizations. This approach handles one-to-many relationships efficiently—where multiple users belong to a single organization. The structured data entry prevents issues with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate city names (Springfield exists in multiple states)&lt;/li&gt;
&lt;li&gt;Spelling variations and typos&lt;/li&gt;
&lt;li&gt;Inconsistent formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Consider the storage implications: storing "Microsoft" as plain text for 10,000 employees means duplicating that string 10,000 times. When the company rebrands, you'll need to update thousands of records individually.&lt;/p&gt;

&lt;p&gt;With a normalized approach using a lookup table (&lt;code&gt;organizations&lt;/code&gt; table with &lt;code&gt;id = 3, name = "Microsoft"&lt;/code&gt;), you only store &lt;code&gt;org_id = 3&lt;/code&gt; for each user. Company name updates require modifying just one record in the reference table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Normalization and Third Normal Form (3NF)
&lt;/h2&gt;

&lt;p&gt;This design pattern follows &lt;strong&gt;Third Normal Form (3NF)&lt;/strong&gt; principles, a fundamental database normalization rule that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eliminates redundant data storage&lt;/li&gt;
&lt;li&gt;Ensures non-key attributes depend only on primary keys&lt;/li&gt;
&lt;li&gt;Improves query performance through proper indexing&lt;/li&gt;
&lt;li&gt;Maintains referential integrity with foreign key constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Organizations lookup table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;organizations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Users table with foreign key&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;org_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways for Backend Development
&lt;/h2&gt;

&lt;p&gt;When building scalable applications, always normalize categorical data into separate lookup tables. This database design pattern reduces storage overhead, simplifies maintenance, and ensures data consistency across your application.&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>sql</category>
      <category>architecture</category>
    </item>
    <item>
      <title>My Key Takeaways from DDIA Chapter 1: Reliability, Scalability, and Maintainability</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Wed, 07 Jan 2026 10:44:12 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/my-key-takeaways-from-ddia-chapter-1-reliability-scalability-and-maintainability-3l49</link>
      <guid>https://dev.to/faizanfirdousi/my-key-takeaways-from-ddia-chapter-1-reliability-scalability-and-maintainability-3l49</guid>
      <description>&lt;p&gt;This was an introductory yet useful chapter to begin with, as it starts with the high-level fundamentals you need to think about before designing systems.&lt;br&gt;
here are my notes which are the things which felt more important for me &lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability:
&lt;/h2&gt;

&lt;p&gt;The application should continue to work even if things go wrong, so design it in a way that handles most of the mistakes users make and places where it could crash. While discussing this stuff, use terms like "resilient" as terms like "fault tolerant" are misleading since there is no system which is 100% fault tolerant.&lt;br&gt;
The difference between fault and failure is that a fault means often one component is off, while failure means the whole system goes down.&lt;/p&gt;

&lt;p&gt;Types of errors the system needs to handle are-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hardware errors (not much for us to worry about, but the errors can also be interconnected with software)&lt;/li&gt;
&lt;li&gt;software errors (bugs in code can cause cascading failures)&lt;/li&gt;
&lt;li&gt;human errors (like wrong config files).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scalability:
&lt;/h2&gt;

&lt;p&gt;It's the system's ability to cope with increased load. It depends on how you design the system so that it can handle more load. Also, it's meaningless to talk like "something is scalable or something doesn't scale."&lt;br&gt;
While talking about load, it's best to define the load termed as load parameters as per your app's requirements the load can be requests per second, ratio of reads and writes to the database, or active users in a chat room.&lt;br&gt;
It's important to manage the system's resources (CPU, memory, etc.) according to when you increase the load parameters.&lt;br&gt;
There are various ways to cope with the load like horizontal and vertical scaling, while keeping in mind that there ain't one scaling strategy or one absolute secret sauce that is applicable for every system as it depends on various factors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency and response time
&lt;/h2&gt;

&lt;p&gt;Latency means the time the data took to travel across the network, while response time means what the user sees—in simple terms, the total time the client sees from sending the request to getting a response. Response time includes more things like network delays, queueing delays, etc.&lt;/p&gt;

&lt;p&gt;The best statistic used to monitor the response times of users is by plotting it in sorted order from fast to slow in percentiles and using the median (also 50th percentile or p50) as a metric. Big companies like Amazon design their internal structure according to the response time of the 99.9th percentile, which means 1 in 1000 requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintainability:
&lt;/h2&gt;

&lt;p&gt;You don't leave the system just after making it and have to maintain it, so while making it you need to keep in mind that it should be easy for maintenance. Important things to keep in mind are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;operability (making it easier for operations teams)&lt;/li&gt;
&lt;li&gt;simplicity (easier to understand, reduce complexity as much as you can by adding right abstractions)&lt;/li&gt;
&lt;li&gt;evolvability (easier to incorporate changes in the future, so it should be modifiable and extensible).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>architecture</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Why Docker Is Not Truly Native on Windows and macOS</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Sat, 29 Nov 2025 05:25:31 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/why-docker-is-not-truly-native-on-windows-and-macos-2af1</link>
      <guid>https://dev.to/faizanfirdousi/why-docker-is-not-truly-native-on-windows-and-macos-2af1</guid>
      <description>&lt;p&gt;Docker isn’t truly native outside Linux, and here’s why.&lt;/p&gt;

&lt;p&gt;If you’re using Docker on Windows or macOS, your containers aren’t running directly on those operating systems. They run inside a small Linux virtual machine, created by Docker Desktop using technologies like WSL2, Hyper-V, or a hypervisor.&lt;/p&gt;

&lt;p&gt;Docker was originally built only for Linux because containerization, at its core, depends on Linux kernel features, specifically namespaces and cgroups.&lt;/p&gt;

&lt;p&gt;Namespaces handle isolation, giving each container its own sandboxed view of system resources like processes, networks, mount points, and filesystems.&lt;/p&gt;

&lt;p&gt;Cgroups (Control Groups) manage and restrict resource usage (CPU, memory, bandwidth, I/O) so containers don’t overload the host.&lt;/p&gt;

&lt;p&gt;Other operating systems simply don’t implement these kernel mechanisms in the same way, which is why Docker can’t run Linux containers directly on Windows or macOS kernels. The workaround? Docker runs a lightweight Linux VM behind the scenes, and everything operates inside it — sharing its kernel, not the actual host OS kernel.&lt;/p&gt;

&lt;p&gt;Yes, Windows containers exist and can run natively on the Windows kernel, but they are a separate container type. The moment you run a Linux container, a Linux kernel layer becomes mandatory, regardless of whether the host machine is running Windows or Mac.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>containers</category>
      <category>docker</category>
    </item>
    <item>
      <title>Understanding Docker Networking: A Practical, Small-Scale Production Emulation</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Sat, 29 Nov 2025 05:23:09 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/understanding-docker-networking-a-practical-small-scale-production-emulation-3doa</link>
      <guid>https://dev.to/faizanfirdousi/understanding-docker-networking-a-practical-small-scale-production-emulation-3doa</guid>
      <description>&lt;p&gt;So I was as usual learning about Docker, and got to know that Docker networking is surprisingly powerful for containerized application design.&lt;/p&gt;

&lt;p&gt;When you create a Docker container, it by default takes up the default bridge network, and the container is assigned a private IP by the internal IPAM (IP Address Management) driver automatically. But you can also create your own user-defined bridge network, and because of that, containers can talk to each other via their container names (service discovery), provided they are in the same network. In the default bridge network, containers can only communicate by IP address.&lt;/p&gt;

&lt;p&gt;I tried creating a small production-style Docker architecture for understanding. Here you have four containers: an API server, web frontend, a database, and a reverse proxy, with three bridge networks: database, backend, and frontend, each playing a specific role in a microservices-like layout.&lt;/p&gt;

&lt;p&gt;The API server is in both the database and backend networks, while the Nginx reverse proxy is in both the frontend and backend networks because it has to route traffic from one port to another. Containers in the same network can communicate and ping each other; others cannot unless you explicitly connect a container to multiple networks.&lt;/p&gt;

&lt;p&gt;The database network is isolated and denied internet access using the --internal flag because it's a convenient way of securing your database. Although the containers inside this internal network can communicate with each other, they cannot access the internet, which adds an extra layer of security for sensitive data.&lt;/p&gt;

&lt;p&gt;The rest of the containers get internet access in the classic but interesting way: each has its own private IP address and is connected to docker0 via virtual Ethernet pairs (veth pairs). The docker0 bridge acts like a virtual switch responsible for routing traffic between containers and the host’s network interface. This is fascinating because it feels like a small-scale simulation of how the internet works, except we have containers instead of physical devices.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
      <category>linux</category>
    </item>
    <item>
      <title>Building a Production-Ready DevOps Pipeline: URL Shortener with Docker, GitHub Actions &amp; AWS</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Sun, 23 Nov 2025 06:40:49 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/building-a-production-ready-devops-pipeline-url-shortener-with-docker-github-actions-aws-3de4</link>
      <guid>https://dev.to/faizanfirdousi/building-a-production-ready-devops-pipeline-url-shortener-with-docker-github-actions-aws-3de4</guid>
      <description>&lt;p&gt;I’ve been exploring Docker and AWS fundamentals for a while, and I finally pushed myself to build a complete, production-style project from scratch—something that aligns with real-world DevOps workflows and cloud-native deployment practices.&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%2F730q36kg18vjnsw5g49l.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%2F730q36kg18vjnsw5g49l.png" alt="url-shortener-image" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a URL shortener service powered by a Go backend API, PostgreSQL as the database engine, Redis for caching, and a lightweight frontend. The structure loosely follows a microservices-oriented pattern to ensure clean separation of components, better scalability, and easier containerized deployment.&lt;/p&gt;

&lt;p&gt;For Infrastructure &amp;amp; Deployment, I containerized all services using Docker with multi-stage builds and orchestrated the full stack using 𝗗𝗼𝗰𝗸𝗲𝗿 𝗖𝗼𝗺𝗽𝗼𝘀𝗲:&lt;/p&gt;

&lt;p&gt;-Frontend&lt;br&gt;
-PostgreSQL&lt;br&gt;
-Redis&lt;br&gt;
-Backend&lt;/p&gt;

&lt;p&gt;I intentionally held off on Kubernetes for now—wanted to master container orchestration fundamentals with Docker Compose before moving into full K8s clusters and cloud orchestration tools. Docker Hub acts as the central container registry, streamlining image management across the CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;Next, I configured 𝗚𝗶𝘁𝗛𝘂𝗯 𝗔𝗰𝘁𝗶𝗼𝗻𝘀 𝗖𝗜/𝗖𝗗 to automate the build, test, and deployment workflow:&lt;/p&gt;

&lt;p&gt;CI pipeline builds the Docker image on every push to main and publishes it to Docker Hub for consistent environment packaging.&lt;/p&gt;

&lt;p&gt;CD pipeline runs on a self-hosted GitHub Actions runner inside an AWS EC2 instance, automatically pulling updated images and redeploying the full stack using Docker Compose.&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%2Fts0jqmtbwijpvdase6mz.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%2Fts0jqmtbwijpvdase6mz.png" alt="CI-pipeline" width="483" height="524"&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%2Fv5u8xajb5rqwzx5d8qww.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%2Fv5u8xajb5rqwzx5d8qww.png" alt="CD-pipeline" width="568" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setup offers smoother, near zero-downtime deployments with proper container health checks and reliable infrastructure automation. It also mirrors how modern engineering teams manage continuous delivery in cloud environments.&lt;/p&gt;

&lt;p&gt;The process came with plenty of challenges—pipeline errors, YAML workflow fixes, and debugging different container services. But the grounding I’ve built in Linux, AWS, networking, Docker, and basic system design helped me move past most hurdles.&lt;/p&gt;

&lt;p&gt;I’m looking forward to building more cloud-based projects, exploring scalable backend systems, and strengthening my DevOps engineering skills.&lt;/p&gt;

&lt;p&gt;GitHub repo: &lt;a href="https://github.com/faizanfirdousi/url_shortener" rel="noopener noreferrer"&gt;https://github.com/faizanfirdousi/url_shortener&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>docker</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>How War Shaped the Foundations of Modern Computer Science</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Fri, 21 Nov 2025 10:27:43 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/how-war-shaped-the-foundations-of-modern-computer-science-3309</link>
      <guid>https://dev.to/faizanfirdousi/how-war-shaped-the-foundations-of-modern-computer-science-3309</guid>
      <description>&lt;p&gt;Well, this is not a very technical post about something, but it's on something that I have learned or come to know in recent times.&lt;/p&gt;

&lt;p&gt;The two major contributing factors in the growth of computer science were war and growth in science.&lt;/p&gt;

&lt;p&gt;Lately, I have been doing some studying and reading a bit about World War 2 and what happened, and I got to know how electronics and computing machines played a very important role in the war. If you know about Oppenheimer, who led the Manhattan Project in Los Alamos, there was a lot of computation needed to make up an atomic bomb. Physicists were not good at doing all of these calculations, so the work for it fell to people who knew physics and programming at the same time. It was at that time that people like von Neumann contributed highly to the projects; with his help, they were able to significantly help the team of the ENIAC computer. Afterward, he helped them design EDVAC's new logical design, now called the von Neumann architecture, which we all know is the basis for all the computers of today.&lt;/p&gt;

&lt;p&gt;Well, if you trace back, von Neumann took inspiration from Alan Turing, whom he met at one point. Turing is another genius whom fewer people know about, who helped the Allied powers crack the Enigma code, which was the most complex encryption machine the Germans made. And according to sources, he made this machine called the Bombe, which was the literal machine that cracked the Enigma machine. Apparently, with the help of this, Alan Turing indirectly reduced the war by 2 years, and nobody knew how it was happening precisely. At that time, he already worked on his concept of the Turing machine, which was an abstract concept that formed the basis for all Computer Science and AI.&lt;/p&gt;

&lt;p&gt;Turing went on to prove the existence of a Universal Turing Machine: a single machine which was capable of simulating any other Turing Machine by reading its program as input. This fundamentally invented the programmable computer, since it showed that a single machine could perform any of a number of tasks simply by modifying its instructions, not its hardware.&lt;/p&gt;

&lt;p&gt;Turing asked the question "Can machines think?" and proposed what became known as the Turing Test.&lt;/p&gt;

&lt;p&gt;The combination of computability theory-what can be computed-and the question of machine thinking-how computers might exhibit intelligence-established the theoretical and philosophical basis on which the whole of computer science and AI were founded. Although born in such a warlike setting, these breakthroughs reach far beyond the fields of battle. Ideas forged by individuals like Turing and von Neumann created frameworks that each modern computer and AI system still follows to this day. What is remarkable is how problems once approached with the urgency of war came to define this peaceful technological world we take for granted. Understanding this history makes the evolution of computer science feel much more like a narrative about how human pressure, intellect, and circumstance shaped the founding moments of an entire field.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>architecture</category>
    </item>
    <item>
      <title>A Developer’s Guide to Terminal Editors: Vim, Nano, and Emacs Explained</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Fri, 21 Nov 2025 10:26:24 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/a-developers-guide-to-terminal-editors-vim-nano-and-emacs-explained-5f4g</link>
      <guid>https://dev.to/faizanfirdousi/a-developers-guide-to-terminal-editors-vim-nano-and-emacs-explained-5f4g</guid>
      <description>&lt;p&gt;you've probably spun up AWS EC2 instances when trying to deploy an app or worked in a Linux-based terminal system. If so, you've likely noticed text editors quite unlike VS Code or what people typically use.&lt;/p&gt;

&lt;p&gt;Let me tell you about terminal-based code editors. Command-line text editors are essential tools for working on virtual servers (like AWS EC2) because these environments typically lack graphical interfaces, requiring administrators to edit configuration files, scripts, and code directly through SSH connections.&lt;/p&gt;

&lt;p&gt;When you SSH into cloud servers (AWS, DigitalOcean, etc.), you only have terminal access without GUI support. In these scenarios, you need lightweight, reliable text editors that can:&lt;br&gt;
 -Edit configuration files quickly during system emergencies&lt;br&gt;
 -Work efficiently on low-resource or remote systems&lt;br&gt;
 -Handle file modifications without requiring file transfers&lt;br&gt;
 -Operate reliably in unstable network conditions&lt;/p&gt;

&lt;p&gt;The three most famous and widely used text editors are:&lt;/p&gt;

&lt;p&gt;Vim: It's the most famous, you've probably heard people bragging about how fast they are with Vim. It's mostly pre-installed on virtually all Linux distributions. It's extremely efficient once mastered, with keyboard shortcuts for rapid text manipulation, but it has a steep learning curve. People configure it extensively according to their preferences, with Neovim nowadays being their daily code editor choice.&lt;/p&gt;

&lt;p&gt;Nano: It's the easiest text editor. Though it doesn't have many features like search functionality like Vim, it's good for users who need immediate productivity without memorizing commands. For system admins or people who don't spend much time with code, Nano works well, but it's only suitable for small files (under 100MB).&lt;/p&gt;

&lt;p&gt;Emacs: It's the most extensible editor, arguably superior to others because it offers almost unlimited customization and can behave like an IDE. It has a whole community dedicated to plugins.&lt;/p&gt;

&lt;p&gt;People debate which is better, but it mostly depends on individual choices.&lt;/p&gt;

&lt;p&gt;For professional server work, knowing at least one of these editors is mandatory because GUI editors are unavailable over SSH connections. Vim is particularly valuable because it's guaranteed to be present on every Linux server. The ability to efficiently edit files remotely without file transfers saves considerable time and reduces errors in production environments.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The Evolution of HTTP: From HTTP/1.1 to HTTP/2 to HTTP/3</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Tue, 02 Sep 2025 17:58:00 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/the-evolution-of-http-from-http11-to-http2-to-http3-5cdf</link>
      <guid>https://dev.to/faizanfirdousi/the-evolution-of-http-from-http11-to-http2-to-http3-5cdf</guid>
      <description>&lt;h2&gt;
  
  
  The Evolution of HTTP: From HTTP/1.1 to HTTP/2 to HTTP/3
&lt;/h2&gt;

&lt;p&gt;We all know that the internet is evolving rapidly, and users increasingly expect everything to load fast. Website loading times have evolved significantly over time, driven by improvements in how browsers retrieve HTML webpages from servers.&lt;/p&gt;

&lt;p&gt;In this blog, I'll discuss everything I've learned about HTTP and its evolution from HTTP/1.1 to HTTP/2, with HTTP/3 still on the horizon for future exploration.&lt;/p&gt;

&lt;p&gt;As we know, everything operates on a client-server model. The more efficient we can make communication between both parties, the greater the benefits we'll achieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview of How HTTP Works
&lt;/h2&gt;

&lt;p&gt;When you enter a website URL in your browser, it sends a GET request to the server, and the server responds with the HTML file that you see displayed.&lt;/p&gt;

&lt;p&gt;In 1996, HTTP/1.0 was released with a simple design. The client sends a &lt;strong&gt;request line&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /index.html HTTP/1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server responds with a &lt;strong&gt;status line&lt;/strong&gt; and headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 1234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While simple, HTTP/1.0 had significant limitations. Remember that HTTP is an application layer protocol that uses TCP underneath, and TCP is inherently slow. In HTTP/1.0, every GET request required establishing a new TCP connection, along with a new TLS handshake for each request. This made performance quite poor. Additionally, there was no TLS defined in the specification itself, SSL/TLS existed but only as a separate convention.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP/1.1
&lt;/h2&gt;

&lt;p&gt;HTTP/1.1 was introduced in 1997 and later refined in 1999. It proved so robust that it survived for over 15 years.&lt;/p&gt;

&lt;p&gt;HTTP/1.1 was a much-improved version of HTTP/1.0. Unlike its predecessor, HTTP/1.1 implemented &lt;strong&gt;persistent connections by default&lt;/strong&gt;. This meant &lt;strong&gt;one TLS handshake per session&lt;/strong&gt;, allowing multiple requests and responses per connection. The &lt;code&gt;Connection: keep-alive&lt;/code&gt; header became implicit, representing a huge performance improvement.&lt;/p&gt;

&lt;p&gt;Advanced caching was another major feature introduced in HTTP/1.1.&lt;/p&gt;

&lt;p&gt;While TLS remained a separate convention in HTTP/1.1, it matured significantly over time. By the late 1990s, SSL had evolved into TLS and was designed to work seamlessly with HTTP/1.1.&lt;/p&gt;

&lt;p&gt;However, as users, we're always seeking better performance...&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP/2
&lt;/h2&gt;

&lt;p&gt;After HTTP/1.1 had been widely used for many years, HTTP/2 was eventually released to address its remaining shortcomings. While HTTP/1.1 was a major improvement over HTTP/1.0 due to its persistent TCP connections, allowing multiple requests and responses over the same connection rather than opening new ones for each resource ,it still had a serious limitation.&lt;/p&gt;

&lt;p&gt;The design still suffered from &lt;strong&gt;head-of-line blocking&lt;/strong&gt;: all responses on a single connection had to be returned in order. If one response was delayed, every other response behind it had to wait. To work around this, web browsers typically opened several parallel TCP connections (usually around six per domain) to fetch multiple resources simultaneously. While this approach helped, it was inefficient because it required extra TCP handshakes, redundant TLS negotiations, and increased network congestion.&lt;/p&gt;

&lt;p&gt;HTTP/2 solved these issues by introducing &lt;strong&gt;multiplexing&lt;/strong&gt;, where multiple requests and responses can truly share a single TCP connection simultaneously without blocking each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Multiplexing Works
&lt;/h3&gt;

&lt;p&gt;In HTTP/2, you don't need to spin up multiple TCP sockets like in HTTP/1.1. Instead, you have &lt;strong&gt;one single TCP connection&lt;/strong&gt;, and within that pipe, you can send all your requests together simultaneously to the server.&lt;/p&gt;

&lt;p&gt;When mixing multiple requests in the same channel, you need a way to track which chunk belongs to which request. That's where &lt;strong&gt;Stream IDs&lt;/strong&gt; come in. Every request the client makes gets tagged with a unique stream ID, and the server does the same when sending responses. Even though all data is interleaved on the wire, both sides can reassemble it correctly.&lt;/p&gt;

&lt;p&gt;Under the hood, HTTP/2 also abandoned the old text-based format and uses a &lt;strong&gt;binary framing layer&lt;/strong&gt;. This means requests and responses are broken into smaller &lt;strong&gt;frames&lt;/strong&gt; (tiny packets of data) that can be interleaved. For example, frames for request A, request B, and request C can travel in mixed order like &lt;code&gt;[A1][B1][A2][C1][B2][A3]&lt;/code&gt;. Upon arrival, the receiver sorts them back based on the stream IDs.&lt;/p&gt;

&lt;p&gt;With this design, one slow response no longer holds up others. Additionally, HTTP/2 compresses repeated headers like cookies and user-agent strings, making it significantly faster and more efficient than HTTP/1.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP/3
&lt;/h2&gt;

&lt;p&gt;But wait, we're still not satisfied! Even though HTTP/2 solved many problems, it still had one fundamental issue lurking underneath , &lt;strong&gt;head-of-line blocking at the TCP layer itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You see, while HTTP/2 eliminated head-of-line blocking at the HTTP layer through multiplexing, it still relied on TCP as its transport protocol. TCP guarantees that packets arrive in order, which sounds great in theory, but it means that if a single TCP packet gets lost somewhere in the network, &lt;strong&gt;every single HTTP/2 stream&lt;/strong&gt; has to wait for that packet to be retransmitted and received before any of them can proceed. So in some cases, this made HTTP/2 even slower than HTTP/1.1!&lt;/p&gt;

&lt;p&gt;This is where HTTP/3 comes in as a revolutionary solution. Instead of trying to patch TCP, HTTP/3 said "forget TCP entirely" and built itself on top of &lt;strong&gt;QUIC&lt;/strong&gt; (Quick UDP Internet Connections), which runs over UDP.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes HTTP/3 Different
&lt;/h2&gt;

&lt;p&gt;HTTP/3 uses QUIC as its transport protocol, and QUIC is fundamentally different from TCP. While TCP establishes one ordered stream of data, QUIC creates &lt;strong&gt;multiple independent streams&lt;/strong&gt; within a single connection. If one stream loses a packet, only that specific stream needs to wait for retransmission, all other streams can continue flowing normally.&lt;/p&gt;

&lt;p&gt;Think of it like this: imagine TCP as a single-lane tunnel where one broken-down car blocks all traffic behind it. QUIC is like a multi-lane highway where each lane operates independently, if there's an accident in one lane, traffic in other lanes keeps moving smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of HTTP/3
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Faster Connection Setup&lt;/strong&gt;: One of the biggest wins with HTTP/3 is connection establishment speed. Traditional HTTP/2 requires separate handshakes for TCP and TLS, which typically takes 2-3 round trips before any actual data can be sent. HTTP/3 combines transport and encryption into a &lt;strong&gt;single handshake&lt;/strong&gt;, reducing connection setup to just one round trip. Even better, for returning visitors, HTTP/3 supports &lt;strong&gt;0-RTT (zero round-trip time)&lt;/strong&gt; resumption, meaning requests can be sent immediately without any handshake delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;True Multiplexing Without Blocking&lt;/strong&gt;: While HTTP/2 implements multiplexing on top of TCP, HTTP/3 leverages QUIC's native multiplexing capabilities. Each resource (like CSS, JavaScript, images) downloads on its own independent stream. If your cat photo loses a packet, your CSS and JavaScript files continue downloading uninterrupted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection Migration&lt;/strong&gt;: This is huge for mobile users. With HTTP/2, when you switch from Wi-Fi to cellular data, your connection breaks and has to be completely re-established. HTTP/3 supports &lt;strong&gt;connection migration&lt;/strong&gt; the same connection seamlessly continues when switching networks. Your downloads don't get interrupted when you walk out of Wi-Fi range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in Security&lt;/strong&gt;: Unlike previous versions where TLS was layered on top, HTTP/3 has &lt;strong&gt;TLS 1.3 encryption built directly into QUIC&lt;/strong&gt;. This eliminates redundant handshakes and provides enhanced security by default.&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%2F3vmxfa6vtaoozj1knyd3.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%2F3vmxfa6vtaoozj1knyd3.png" alt=" " width="347" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How HTTP/3 is Currently Used
&lt;/h2&gt;

&lt;p&gt;As of 2025, HTTP/3 adoption has been growing rapidly. According to recent data, &lt;strong&gt;over 95% of major browsers now support HTTP/3&lt;/strong&gt;, and it's being used by approximately &lt;strong&gt;26-34% of the top websites&lt;/strong&gt; worldwide. Major players like Google, Facebook, Cloudflare, and YouTube have already implemented HTTP/3.&lt;/p&gt;

&lt;p&gt;Chrome enabled HTTP/3 by default back in 2020, Firefox followed in 2021, and Safari has support but keeps it disabled by default (typical Apple behavior, honestly). On the server side, web servers like LiteSpeed, Nginx, and even Microsoft IIS have working HTTP/3 implementations.&lt;/p&gt;

&lt;p&gt;The interesting thing is that you've probably already used HTTP/3 without realizing it. If you're using Chrome and visiting Google services, YouTube, or any Cloudflare-protected site, you're likely already experiencing HTTP/3's benefits.&lt;/p&gt;

&lt;p&gt;However, adoption isn't universal yet. Many smaller websites and older infrastructure still haven't made the switch, partly because the performance benefits are most noticeable for high-traffic sites or users on unreliable networks. For simple websites that are already "good enough," the complexity of upgrading might not seem worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Beautiful Evolution of Internet Infrastructure
&lt;/h2&gt;

&lt;p&gt;Isn't it fascinating how something as simple as loading a webpage has evolved into such a sophisticated dance of protocols and optimizations?&lt;/p&gt;

&lt;p&gt;When you hit Enter after typing a URL, you're not just requesting a simple HTML file anymore. Behind that seemingly simple action, there's this beautiful orchestration of technology that has evolved over decades. Your browser is making intelligent decisions about which protocol version to use, establishing encrypted connections in milliseconds, multiplexing dozens of resources simultaneously across independent streams, and seamlessly handling network switches, all to get that webpage to you as fast as possible.&lt;/p&gt;

&lt;p&gt;What started as a simple request-response pattern in HTTP/1.0 has evolved into this incredibly efficient system where your browser can download images, stylesheets, scripts, and fonts all at once, without any of them blocking each other, over a single connection that maintains itself even when you switch from Wi-Fi to cellular data.&lt;/p&gt;

&lt;p&gt;The journey from HTTP/1.1's persistent connections to HTTP/2's multiplexing to HTTP/3's QUIC-based streams represents decades of engineers identifying bottlenecks and innovating solutions. Each generation didn't just add features, it fundamentally reimagined how data should flow across the internet.&lt;/p&gt;

&lt;p&gt;And the best part? This entire evolution has been largely invisible to users. The same simple action of clicking a link or typing a URL now delivers an exponentially better experience, thanks to all this networking magic happening behind the scenes. It's a testament to how the internet continues to evolve and optimize itself while maintaining the simplicity that makes it accessible to everyone.&lt;/p&gt;

&lt;p&gt;That's the beauty of internet evolution, constant improvement in the background, making every webpage load a little bit faster, a little bit more reliable, and a little bit more secure than before.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How a Simple Bitwise AND Decides If Two Computers Can Talk Directly</title>
      <dc:creator>Faizan Firdousi</dc:creator>
      <pubDate>Fri, 01 Aug 2025 06:28:46 +0000</pubDate>
      <link>https://dev.to/faizanfirdousi/how-a-simple-bitwise-and-decides-if-two-computers-can-talk-directly-4i3</link>
      <guid>https://dev.to/faizanfirdousi/how-a-simple-bitwise-and-decides-if-two-computers-can-talk-directly-4i3</guid>
      <description>&lt;p&gt;Do you know one of the most practical uses of bitwise operations in computer networking?&lt;br&gt;
It happens every time your computer needs to determine whether another computer is on the same local network or a different one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Role of Bitwise Operations in Networking
&lt;/h2&gt;

&lt;p&gt;When a computer wants to communicate with another device, it doesn’t just blindly send data. Instead, it performs a quick logical check to decide if the destination device is on the same LAN (Local Area Network) or somewhere else on the internet.&lt;/p&gt;

&lt;p&gt;This check is done using a bitwise AND operation.&lt;/p&gt;

&lt;p&gt;How the Bitwise AND Operation Works&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Get the IP Address and Subnet Mask – Every computer on a network has an IP address and a subnet mask.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Perform a Bitwise AND – The computer uses its own IP address and subnet mask to perform a logical AND operation, which produces the network address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat for the Destination IP – The same AND operation is applied to the destination IP address using the same subnet mask.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compare the Results – If both network addresses match, the devices are on the same network. If they don’t, they are on different networks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;p&gt;If your IP address is 192.168.1.10 and your subnet mask is 255.255.255.0, a bitwise AND gives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.1.10
AND 255.255.255.0
= 192.168.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same calculation is done for the destination IP. If it results in the same network address (192.168.1.0 in this case), the computer knows the device is local.&lt;/p&gt;

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

&lt;p&gt;If the destination is local, the computer sends data directly to it.&lt;br&gt;
If it’s outside the local network, the data is sent to the default gateway (usually a router), which then forwards it toward the correct destination.&lt;/p&gt;

&lt;p&gt;While switches and routers help in actually forwarding packets, this bitwise AND decision happens inside your computer before sending the packet.&lt;/p&gt;

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

&lt;p&gt;Bitwise operations aren’t just for programmers — they’re essential for network communication.&lt;/p&gt;

&lt;p&gt;The logical AND operation with a subnet mask determines if two devices are on the same network.&lt;/p&gt;

&lt;p&gt;This happens before data leaves your computer.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>cloud</category>
      <category>devops</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
