<?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: Backend By Dmytro</title>
    <description>The latest articles on DEV Community by Backend By Dmytro (@backendbydmytro).</description>
    <link>https://dev.to/backendbydmytro</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%2F3443234%2F1e129dbf-f5ba-4a8a-83b2-3ed2179f0e3d.png</url>
      <title>DEV Community: Backend By Dmytro</title>
      <link>https://dev.to/backendbydmytro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/backendbydmytro"/>
    <language>en</language>
    <item>
      <title>Docker Containers Aren't Magic Boxes: Seeing Linux Namespaces in Action</title>
      <dc:creator>Backend By Dmytro</dc:creator>
      <pubDate>Fri, 27 Feb 2026 13:32:39 +0000</pubDate>
      <link>https://dev.to/backendbydmytro/docker-containers-arent-magic-boxes-seeing-linux-namespaces-in-action-3dob</link>
      <guid>https://dev.to/backendbydmytro/docker-containers-arent-magic-boxes-seeing-linux-namespaces-in-action-3dob</guid>
      <description>&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/7XRR704YEgs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;You restart a container with one extra flag—&lt;code&gt;--pid=host&lt;/code&gt;—and suddenly &lt;code&gt;top&lt;/code&gt; inside it shows every process on the host. Nothing else changed. That's the moment the "container as isolated VM" mental model breaks down, and the real one has to replace it.&lt;/p&gt;

&lt;p&gt;Containers aren't virtual machines. They don't have a separate kernel. They're Linux processes whose view of the system — process table, filesystem, users, network interfaces — is scoped by kernel namespaces. Change the namespace configuration, and what the process can see changes. That's the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Runtime Chain (and Where Namespaces Attach)
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;docker run&lt;/code&gt;, you're triggering a chain of processes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dockerd → containerd → containerd-shim → runc → your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The critical piece is &lt;code&gt;runc&lt;/code&gt;. It's short-lived—it configures the namespaces, forks the container process (say, nginx), then exits. The containerd-shim process sticks around to supervise that process and hold onto its file descriptors. After runc finishes, the container is just nginx running under a set of kernel namespace constraints.&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%2Fdr0t5hpuse507znzjfd6.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%2Fdr0t5hpuse507znzjfd6.png" alt="Component view of Docker/container runtime processes and the Linux namespaces they configure and use for isolation."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The namespaces that &lt;code&gt;runc&lt;/code&gt; process configures - such as &lt;code&gt;PID&lt;/code&gt;, &lt;code&gt;mount&lt;/code&gt;, &lt;code&gt;net&lt;/code&gt;, and optionally user, are attached to the application process. They're not a property of Docker. They're a property of the process.&lt;/p&gt;

&lt;p&gt;You can inspect them directly:&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="nv"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.Pid}}'&lt;/span&gt; my-nginx&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;sudo ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /proc/&lt;span class="nv"&gt;$PID&lt;/span&gt;/ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/ns&lt;/code&gt; listing is your ground truth. Everything else follows from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  PID Namespace: Process Visibility Is a Configuration Choice
&lt;/h2&gt;

&lt;p&gt;Run nginx normally, exec in, and run &lt;code&gt;top&lt;/code&gt;. You'll see only nginx master and workers—nothing from the host. That's the PID namespace working: the process has its own view of the process table, starting from PID 1.&lt;/p&gt;

&lt;p&gt;Now restart with &lt;code&gt;--pid=host&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-nginx &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="nt"&gt;-d&lt;/span&gt; nginx:latest
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; my-nginx bash
apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; procps
top
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;top&lt;/code&gt; now shows the host's full process list. The container joined the host PID namespace—there's no isolation there anymore.&lt;/p&gt;

&lt;p&gt;The debugging implication: if a tool inside a container "can't see" a process, or if it can see more than you expected, check whether &lt;code&gt;--pid=host&lt;/code&gt; is set. Don't assume the default.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mount Namespace: Same Machine, Different Filesystem
&lt;/h2&gt;

&lt;p&gt;Start a default nginx container and compare OS identity inside vs. outside:&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;# Inside the container&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release   &lt;span class="c"&gt;# → Debian&lt;/span&gt;

&lt;span class="c"&gt;# On the host&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release   &lt;span class="c"&gt;# → Fedora (or whatever the host runs)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same machine, different filesystem roots. The container's &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; exists in the container's mount view but doesn't show up at &lt;code&gt;/etc/nginx&lt;/code&gt; on the host—it lives in Docker's image storage layer and is mounted into the container's isolated namespace.&lt;/p&gt;

&lt;p&gt;The practical lesson here is about file path reasoning: paths that exist in the container may not exist at the same location on the host, and vice versa. If you're troubleshooting config files or bind mounts, you need to reason about &lt;em&gt;which mount table&lt;/em&gt; you're looking at.&lt;/p&gt;




&lt;h2&gt;
  
  
  User Namespace: Whether Container Root Is Host Root
&lt;/h2&gt;

&lt;p&gt;By default, Docker does &lt;strong&gt;not&lt;/strong&gt; use user namespaces. The consequence is direct: root in the container is root on the host for any host-mounted paths.&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/userns-demo
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-nginx &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/userns-demo:/demo &lt;span class="nt"&gt;-d&lt;/span&gt; nginx:latest
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; my-nginx bash
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /demo/container_file.txt
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /demo/container_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /tmp/userns-demo
&lt;span class="c"&gt;# → owned by root, mode 600&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A non-root user on the host can't touch that file. Root in the container wrote it as host root.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling &lt;code&gt;userns-remap&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Add this to &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"userns-remap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Docker. Now the container runs under a &lt;code&gt;dockremap&lt;/code&gt; user that has a &lt;strong&gt;subordinate UID range&lt;/strong&gt;—typically 100000–165536—assigned in &lt;code&gt;/etc/subuid&lt;/code&gt;. Container UID 0 maps to host UID 100000, UID 1 maps to 100001, and so on.&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%2Ftwcbuagg6ejeic4ky76w.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%2Ftwcbuagg6ejeic4ky76w.png" alt="Diagram showing how a host user maps to subordinate UID ranges and then to container UIDs."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can verify the mapping by &lt;code&gt;chown&lt;/code&gt;-ing the bind-mounted directory on the host and watching how the container perceives it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 100000:100000 /tmp/userns-demo
&lt;span class="c"&gt;# Container sees: owned by root (UID 0)&lt;/span&gt;

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1001003:1001003 /tmp/userns-demo
&lt;span class="c"&gt;# Container sees: owned by UID 1003&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The offset is consistent and predictable. What changes is whether a container process that runs as "root" actually has host-root-level access to host-mounted paths—and with &lt;code&gt;userns-remap&lt;/code&gt;, it doesn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Network Namespace: Interfaces, Isolation, and &lt;code&gt;--network=host&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;By default, the container gets its own network namespace with a limited interface set. The host side has a &lt;code&gt;docker0&lt;/code&gt; bridge; each container gets a &lt;code&gt;veth&lt;/code&gt; pair—one end in the host namespace attached to the bridge, the other end in the container's namespace exposed as &lt;code&gt;eth0&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%2Fppps8b7h5ej3bertr4lq.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%2Fppps8b7h5ej3bertr4lq.png" alt="Illustrates how a container interface connects to the host via a bridge and a veth pair, with iptables shown along the path."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traffic from the container reaches the internet through that bridge, with &lt;code&gt;iptables&lt;/code&gt; handling NAT on the way out.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;--network=host&lt;/code&gt;, all of that is bypassed. The container shares the host's network namespace entirely:&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;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-nginx &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="nt"&gt;-d&lt;/span&gt; nginx:latest
&lt;span class="c"&gt;# ip addr inside container == ip addr on host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same output, same interfaces. No veth, no bridge, no NAT. The process is just using the host network stack directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Checklist
&lt;/h2&gt;

&lt;p&gt;When something feels "leaky" or unexpectedly isolated, these are the four questions worth asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PID visibility off?&lt;/strong&gt; Check if &lt;code&gt;--pid=host&lt;/code&gt; is set; inspect &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/ns/pid&lt;/code&gt; on both container and host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong filesystem path?&lt;/strong&gt; Verify which mount namespace you're in; don't assume host and container paths agree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind mount permissions wrong?&lt;/strong&gt; Check whether &lt;code&gt;userns-remap&lt;/code&gt; is enabled and what UID mapping applies to the path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network interface missing or unexpected?&lt;/strong&gt; Confirm whether the container uses an isolated network namespace or &lt;code&gt;--network=host&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these require special tooling—just &lt;code&gt;/proc&lt;/code&gt;, &lt;code&gt;ip addr&lt;/code&gt;, and &lt;code&gt;ls -la&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;A container is a Linux process. The isolation you observe isn't inherent to Docker—it's a set of namespace configurations that runc applies when the container starts. Those configurations are visible, inspectable, and explicitly changeable.&lt;/p&gt;

&lt;p&gt;When isolation breaks in unexpected ways, it's almost always because a namespace is being shared (&lt;code&gt;--pid=host&lt;/code&gt;, &lt;code&gt;--network=host&lt;/code&gt;) or because UID mapping isn't set up the way you assumed. Check the namespaces, then reason from there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/7XRR704YEgs" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Watch the full video walkthrough (diagrams + demo)&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Docker does not exist: processes and reliability</title>
      <dc:creator>Backend By Dmytro</dc:creator>
      <pubDate>Sat, 21 Feb 2026 16:03:31 +0000</pubDate>
      <link>https://dev.to/backendbydmytro/docker-does-not-exist-processes-and-reliability-4cip</link>
      <guid>https://dev.to/backendbydmytro/docker-does-not-exist-processes-and-reliability-4cip</guid>
      <description>&lt;p&gt;Stop me if this has happened to you: you restart the Docker service expecting your Nginx container to die, and it just... keeps serving traffic. That's not a bug. It's the architecture telling you something important about what a "container" actually is.&lt;/p&gt;

&lt;p&gt;Here's the mental model shift: a Docker container isn't a VM-like object managed by Docker. It's an application process running on the host kernel, supervised by a chain of other processes—&lt;code&gt;dockerd&lt;/code&gt;, &lt;code&gt;containerd&lt;/code&gt;, &lt;code&gt;containerd-shim&lt;/code&gt;, and &lt;code&gt;runc&lt;/code&gt;. Docker as the brand is real; Docker the monolithic runtime is not.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/tdALRTemTfU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Everything below runs on Fedora Linux. MacOS/Windows users: Docker runs a Linux VM under the hood, so the process tree will be inside that VM, not directly on your host.&lt;/p&gt;

&lt;p&gt;First, enable &lt;code&gt;live-restore&lt;/code&gt; in &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;— this is what lets containers survive a Docker Engine restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"live-restore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart Docker and spin up Nginx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker restart
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-nginx &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;-d&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the Host Actually Sees
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-ef&lt;/span&gt; &lt;span class="nt"&gt;--forest&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'docker|containerd|nginx'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output isn't a single "container" process. You'll see a tree: &lt;code&gt;dockerd&lt;/code&gt; → &lt;code&gt;containerd&lt;/code&gt; → &lt;code&gt;containerd-shim&lt;/code&gt; → &lt;code&gt;nginx&lt;/code&gt;. Four separate processes on the host, each with a distinct job. That tree structure is the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  dockerd: REST API and Manager, Not a Runtime
&lt;/h2&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%2F3dxdfnxvdfko1wwgnj4c.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%2F3dxdfnxvdfko1wwgnj4c.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;docker run ...&lt;/code&gt;, the CLI translates it into a REST API request sent to &lt;code&gt;dockerd&lt;/code&gt;. That's it — the CLI is a thin HTTP client.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dockerd&lt;/code&gt; handles image management, volume management, and port-mapping (via a spawned &lt;code&gt;docker-proxy&lt;/code&gt; process). What it doesn't do is actually run your container. For that, it makes gRPC calls to &lt;code&gt;containerd&lt;/code&gt; over &lt;code&gt;/run/containerd/containerd.sock&lt;/code&gt;. Think of &lt;code&gt;dockerd&lt;/code&gt; as the control plane; the execution happens elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  containerd: The Actual Runtime
&lt;/h2&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%2Fylw32c7xi7lrrt47lntj.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%2Fylw32c7xi7lrrt47lntj.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;containerd&lt;/code&gt; is where execution begins. It's OCI-compliant, which is why Kubernetes can use it directly without Docker at all.&lt;/p&gt;

&lt;p&gt;For each container start, &lt;code&gt;containerd&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pulls the image from the registry,&lt;/li&gt;
&lt;li&gt;prepares the root filesystem,&lt;/li&gt;
&lt;li&gt;spawns a &lt;code&gt;containerd-shim&lt;/code&gt; process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shim is the key piece. That's what makes the restart behavior make sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  containerd-shim: The Per-Container Supervisor
&lt;/h2&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%2Fagbyvqtvo6a8twzwq76g.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%2Fagbyvqtvo6a8twzwq76g.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each container gets exactly one &lt;code&gt;containerd-shim&lt;/code&gt;. Its responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs &lt;code&gt;runc&lt;/code&gt; to set up Linux namespaces and cgroups, then &lt;code&gt;runc&lt;/code&gt; exits—it's just a setup tool&lt;/li&gt;
&lt;li&gt;Holds the stdin/stdout/stderr file descriptors for the container process (that's how &lt;code&gt;docker logs&lt;/code&gt; and &lt;code&gt;docker attach&lt;/code&gt; work)&lt;/li&gt;
&lt;li&gt;Relays signals like &lt;code&gt;SIGTERM&lt;/code&gt; to the application&lt;/li&gt;
&lt;li&gt;Acts as the direct parent of your app process—so the app stays alive even if &lt;code&gt;containerd&lt;/code&gt; or &lt;code&gt;dockerd&lt;/code&gt; goes away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is the one most people don't expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof: Stop Docker, Container Keeps Running
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop docker
ps &lt;span class="nt"&gt;-ef&lt;/span&gt; &lt;span class="nt"&gt;--forest&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'docker|containerd|nginx'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dockerd&lt;/code&gt; is gone. &lt;code&gt;containerd&lt;/code&gt;, &lt;code&gt;containerd-shim&lt;/code&gt;, and &lt;code&gt;nginx&lt;/code&gt; are still there. With &lt;code&gt;live-restore: true&lt;/code&gt;, the shim holds the process alive independently of the engine.&lt;/p&gt;

&lt;p&gt;Go further—kill &lt;code&gt;containerd&lt;/code&gt; itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo kill&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-o&lt;/span&gt; containerd&lt;span class="si"&gt;)&lt;/span&gt;
ps &lt;span class="nt"&gt;-ef&lt;/span&gt; &lt;span class="nt"&gt;--forest&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'containerd|nginx'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shim and Nginx survive. The shim is the direct parent; &lt;code&gt;containerd&lt;/code&gt; dying is irrelevant to the running workload.&lt;/p&gt;

&lt;p&gt;This is what makes zero-downtime Docker Engine upgrades possible: &lt;code&gt;live-restore&lt;/code&gt; + the shim architecture means your app doesn't care what the daemons are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filesystem: No Hard Boundary Against Host Root
&lt;/h2&gt;

&lt;p&gt;Containers provide process isolation—not a security boundary against a root user on the host. Here's proof:&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="nv"&gt;NGINX_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;pgrep &lt;span class="nt"&gt;-o&lt;/span&gt; nginx&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /proc/&lt;span class="nv"&gt;$NGINX_PID&lt;/span&gt;/root/etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the container's Nginx config, read directly from the host via &lt;code&gt;/proc&lt;/code&gt;. No volume mounts, no &lt;code&gt;docker exec&lt;/code&gt;. The container's filesystem is just a view the kernel provides; &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/root&lt;/code&gt; exposes it to any sufficiently privileged host process.&lt;/p&gt;

&lt;p&gt;Security implication: if the host is compromised at root level, assume all containers on that host are too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Changes Operationally
&lt;/h2&gt;

&lt;p&gt;A few places where this model pays off immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debugging "container still running after Docker restart"&lt;/strong&gt;: check for &lt;code&gt;containerd-shim&lt;/code&gt; and your app process on the host—they're just &lt;code&gt;ps&lt;/code&gt; entries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port-mapping issues&lt;/strong&gt;: &lt;code&gt;docker-proxy&lt;/code&gt; is a real process; it shows up in &lt;code&gt;ss&lt;/code&gt; or &lt;code&gt;lsof&lt;/code&gt; output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engine upgrades&lt;/strong&gt;: &lt;code&gt;live-restore: true&lt;/code&gt; + a shim-aware mental model = no forced downtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security reviews&lt;/strong&gt;: treat host root access as full container access, because &lt;code&gt;/proc&lt;/code&gt; makes it so&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next time you type &lt;code&gt;docker run&lt;/code&gt;, what's actually happening is: the CLI talks to an API, the API tells a runtime, the runtime spawns a supervisor, the supervisor sets up isolation via &lt;code&gt;runc&lt;/code&gt; and then stays out of the way. Your Nginx is just a process. And that's exactly what makes containers fast, composable, and debuggable with standard Linux tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/tdALRTemTfU" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Watch the full video walkthrough (diagrams + demo)&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>linux</category>
      <category>backend</category>
    </item>
    <item>
      <title>My First Contribution to Symfony (and Why You Should Start Small in Open Source)</title>
      <dc:creator>Backend By Dmytro</dc:creator>
      <pubDate>Wed, 24 Sep 2025 17:35:41 +0000</pubDate>
      <link>https://dev.to/backendbydmytro/my-first-contribution-to-symfony-and-why-you-should-start-small-in-open-source-hh8</link>
      <guid>https://dev.to/backendbydmytro/my-first-contribution-to-symfony-and-why-you-should-start-small-in-open-source-hh8</guid>
      <description>&lt;p&gt;This week, I made my very first contribution to the Symfony Framework — and it got merged! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/symfony/symfony/issues/61643" rel="noopener noreferrer"&gt;Pull Request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The change itself was small: a correction in translations. But the importance for me is huge: it marks the beginning of consistent contributions to the open source ecosystem.&lt;/p&gt;

&lt;p&gt;Why does this matter?&lt;/p&gt;

&lt;p&gt;Because Symfony is the foundation of much of my professional work.&lt;/p&gt;

&lt;p&gt;Because giving back strengthens both the framework and the community.&lt;/p&gt;

&lt;p&gt;Because small steps build momentum — no one starts with a huge feature.&lt;/p&gt;

&lt;p&gt;If you’re considering contributing to open source but feel overwhelmed: don’t wait. Fixing a typo, clarifying docs, or improving a translation is already valuable.&lt;/p&gt;

&lt;p&gt;I’ll continue documenting my journey and sharing tips on how to get involved with Symfony and backend development in general.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>php</category>
      <category>opensource</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
