<?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: Michael Wolfenberger</title>
    <description>The latest articles on DEV Community by Michael Wolfenberger (@michaelwolfenberger).</description>
    <link>https://dev.to/michaelwolfenberger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4078126%2F8d45d735-ac9f-4c90-a23c-a4fcc6c5bb54.jpg</url>
      <title>DEV Community: Michael Wolfenberger</title>
      <link>https://dev.to/michaelwolfenberger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaelwolfenberger"/>
    <language>en</language>
    <item>
      <title>Docker Under the Hood: Namespaces, Cgroups, and Layered Filesystems Explained</title>
      <dc:creator>Michael Wolfenberger</dc:creator>
      <pubDate>Sun, 06 Sep 2026 17:49:22 +0000</pubDate>
      <link>https://dev.to/michaelwolfenberger/docker-under-the-hood-namespaces-cgroups-and-layered-filesystems-explained-1414</link>
      <guid>https://dev.to/michaelwolfenberger/docker-under-the-hood-namespaces-cgroups-and-layered-filesystems-explained-1414</guid>
      <description>&lt;h2&gt;
  
  
  The Virtual Machine Misconception
&lt;/h2&gt;

&lt;p&gt;Many engineers approach Docker thinking it operates like a lightweight virtual machine. It does not. &lt;/p&gt;

&lt;p&gt;A virtual machine runs a hypervisor that virtualizes physical hardware (CPUs, memory controllers, network adapters). Inside that simulated hardware lives a full guest operating system with its own independent kernel, init system, device drivers, and system background services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------+       +-----------------------------+
|      Virtual Machine        |       |          Container          |
+-----------------------------+       +-----------------------------+
| App A       | App B         |       | App A       | App B         |
| Bin/Libs    | Bin/Libs      |       | Bin/Libs    | Bin/Libs      |
| Guest OS (Full Kernel)      |       | Root Filesystem (No Kernel) |
| Hypervisor (Hardware Sim)   |       | Linux Namespaces + Cgroups  |
+-----------------------------+       +-----------------------------+
| Host OS (Linux/Darwin/NT)   |       | Host Linux Kernel           |
| Bare Metal Hardware         |       | Bare Metal Hardware         |
+-----------------------------+       +-----------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Docker container executes directly on the host Linux kernel. When you run &lt;code&gt;docker run -d nginx&lt;/code&gt;, the Nginx worker is an ordinary process visible in the host machine’s process tree. The difference is that the kernel restricts what that process can &lt;strong&gt;see&lt;/strong&gt;, what resources it can &lt;strong&gt;consume&lt;/strong&gt;, and what files it can &lt;strong&gt;modify&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Restricting Visibility: Linux Namespaces
&lt;/h2&gt;

&lt;p&gt;Linux namespaces govern boundary isolation by restricting what system resources a process can observe.&lt;/p&gt;

&lt;p&gt;Docker relies on six primary namespaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pid&lt;/code&gt; (Process IDs):&lt;/strong&gt; Assigns an isolated process hierarchy. Inside the container, your main application process appears as &lt;code&gt;PID 1&lt;/code&gt;. On the host, it runs under an ordinary host PID (e.g., &lt;code&gt;PID 14382&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;net&lt;/code&gt; (Networking):&lt;/strong&gt; Provides dedicated virtual network interfaces, independent routing tables, and isolated firewall/iptables rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mnt&lt;/code&gt; (Mount points):&lt;/strong&gt; Isolates the filesystem mount table so the process only sees its own root directory (&lt;code&gt;/&lt;/code&gt;), completely separated from the host storage tree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ipc&lt;/code&gt; (Inter-Process Communication):&lt;/strong&gt; Prevents containers from accessing shared memory segments or message queues belonging to other host processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;uts&lt;/code&gt; (UNIX Timesharing System):&lt;/strong&gt; Enables each container to define its own hostname and domain name independently of the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;user&lt;/code&gt; (User IDs):&lt;/strong&gt; Maps an unprivileged user inside the container to a distinct UID on the host (e.g., container &lt;code&gt;root&lt;/code&gt; mapped to an unprivileged UID on the host system).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing Namespace Isolation Manually
&lt;/h3&gt;

&lt;p&gt;You do not need Docker to spin up an isolated process environment. The native Linux &lt;code&gt;unshare&lt;/code&gt; utility creates namespaces directly from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Spawn a new shell inside an isolated PID and mount namespace&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;unshare &lt;span class="nt"&gt;--fork&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt; &lt;span class="nt"&gt;--mount-proc&lt;/span&gt; /bin/bash

&lt;span class="c"&gt;# View running processes from within the new namespace&lt;/span&gt;
ps aux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside that shell, &lt;code&gt;ps aux&lt;/code&gt; will only show two processes: the &lt;code&gt;/bin/bash&lt;/code&gt; shell running as &lt;code&gt;PID 1&lt;/code&gt; and the &lt;code&gt;ps&lt;/code&gt; command itself. The rest of the host system's running processes are completely invisible.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Metering Consumption: Control Groups (cgroups v2)
&lt;/h2&gt;

&lt;p&gt;If namespaces dictate what a process can &lt;em&gt;see&lt;/em&gt;, control groups (cgroups) dictate what a process can &lt;em&gt;consume&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Without resource boundaries, a runaway container could exhaust available system memory, causing the Linux Out-Of-Memory (OOM) killer to terminate critical host services, or consume 100% of CPU time through unbounded loops.&lt;/p&gt;

&lt;p&gt;In modern Linux distributions using &lt;strong&gt;cgroup v2&lt;/strong&gt;, resource constraints are configured through a unified pseudo-filesystem mounted at &lt;code&gt;/sys/fs/cgroup&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cpu.max&lt;/code&gt;:&lt;/strong&gt; Enforces CPU bandwidth limits (e.g., allocating a quota of 50,000 microseconds per 100,000-microsecond period throttles the container to 0.5 CPU cores).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;memory.max&lt;/code&gt;:&lt;/strong&gt; Establishes hard memory ceilings. If processes inside that cgroup exceed this threshold, the kernel terminates processes within &lt;em&gt;that group only&lt;/em&gt;, leaving host services untouched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;io.max&lt;/code&gt;:&lt;/strong&gt; Caps disk read and write throughput and IOPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you specify runtime constraints in Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; bounded-worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"512m"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  my-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container runtime writes those exact numerical thresholds into &lt;code&gt;/sys/fs/cgroup/docker/&amp;lt;container-id&amp;gt;/memory.max&lt;/code&gt; and &lt;code&gt;cpu.max&lt;/code&gt;. The Linux kernel scheduler handles the actual enforcement at the hardware level.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Layered Storage: OverlayFS and Copy-on-Write
&lt;/h2&gt;

&lt;p&gt;If every container required an independent duplicate of the operating system root filesystem, provisioning would be slow and drive storage would rapidly fill up. Docker achieves near-instant startup times and minimal disk overhead through &lt;strong&gt;OverlayFS&lt;/strong&gt;, a union mount filesystem that implements Copy-on-Write (CoW).&lt;/p&gt;

&lt;p&gt;An OverlayFS mount layers multiple directories and presents them to the container as a unified directory tree:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lowerdir&lt;/code&gt;:&lt;/strong&gt; A stack of read-only image layers. Multiple running containers share the exact same read-only base layers on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upperdir&lt;/code&gt;:&lt;/strong&gt; A thin, isolated read-write directory created specifically for the running container instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;workdir&lt;/code&gt;:&lt;/strong&gt; An internal staging directory used by the kernel for atomic operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;merged&lt;/code&gt;:&lt;/strong&gt; The unified mount point that the container process actually interacts with.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       [ Merged View: / ]
      /                  \
+------------------------------+
|   Upper Layer (Read/Write)   |  &amp;lt;- Container runtime changes &amp;amp; new files
+------------------------------+
|   Lower Layer 2 (Read-Only)  |  &amp;lt;- Application binaries &amp;amp; runtime
+------------------------------+
|   Lower Layer 1 (Read-Only)  |  &amp;lt;- Base OS rootfs (Alpine, Debian, etc.)
+------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a container modifies a file that originated in a lower layer, OverlayFS copies the file up into &lt;code&gt;upperdir&lt;/code&gt; before applying the modification. The underlying base layer remains pristine, allowing dozens of containers to boot from a shared image without cross-contamination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simulating an Overlay Mount in Bash
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create directory structure&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; lower upper work merged

&lt;span class="c"&gt;# Populate base read-only layer&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"base file v1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; lower/app.conf

&lt;span class="c"&gt;# Mount using OverlayFS&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; overlay overlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;lowerdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lower,upperdir&lt;span class="o"&gt;=&lt;/span&gt;upper,workdir&lt;span class="o"&gt;=&lt;/span&gt;work &lt;span class="se"&gt;\&lt;/span&gt;
  merged

&lt;span class="c"&gt;# Verify the unified view&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;merged/app.conf  &lt;span class="c"&gt;# Outputs: base file v1&lt;/span&gt;

&lt;span class="c"&gt;# Modify the file within the merged mount&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"modified for container"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; merged/app.conf

&lt;span class="c"&gt;# The base file remains untouched; modifications live exclusively in upper&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;lower/app.conf   &lt;span class="c"&gt;# Outputs: base file v1&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;upper/app.conf   &lt;span class="c"&gt;# Outputs: modified for container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Docker is not a hardware emulator. It is an orchestration wrapper that configures three mature Linux kernel subsystems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Namespaces&lt;/strong&gt; establish isolation boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cgroups&lt;/strong&gt; meter resource utilization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OverlayFS&lt;/strong&gt; delivers fast, copy-on-write storage layers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When troubleshooting container behavior—whether debugging DNS resolution, handling OOM terminations, or inspecting disk consumption—the solution is found in how the Linux kernel isolates and schedules ordinary processes.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>containers</category>
      <category>docker</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
