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.
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—dockerd, containerd, containerd-shim, and runc. Docker as the brand is real; Docker the monolithic runtime is not.
The Setup
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.
First, enable live-restore in /etc/docker/daemon.json— this is what lets containers survive a Docker Engine restart:
{
"live-restore": true
}
Then restart Docker and spin up Nginx:
sudo service docker restart
docker run --rm --name=my-nginx -p 8080:80 -d nginx
What the Host Actually Sees
ps -ef --forest | grep -E 'docker|containerd|nginx' | grep -v grep
The output isn't a single "container" process. You'll see a tree: dockerd → containerd → containerd-shim → nginx. Four separate processes on the host, each with a distinct job. That tree structure is the whole story.
dockerd: REST API and Manager, Not a Runtime
When you run docker run ..., the CLI translates it into a REST API request sent to dockerd. That's it — the CLI is a thin HTTP client.
dockerd handles image management, volume management, and port-mapping (via a spawned docker-proxy process). What it doesn't do is actually run your container. For that, it makes gRPC calls to containerd over /run/containerd/containerd.sock. Think of dockerd as the control plane; the execution happens elsewhere.
containerd: The Actual Runtime
containerd is where execution begins. It's OCI-compliant, which is why Kubernetes can use it directly without Docker at all.
For each container start, containerd:
- pulls the image from the registry,
- prepares the root filesystem,
- spawns a
containerd-shimprocess.
The shim is the key piece. That's what makes the restart behavior make sense.
containerd-shim: The Per-Container Supervisor
Each container gets exactly one containerd-shim. Its responsibilities:
- Runs
runcto set up Linux namespaces and cgroups, thenruncexits—it's just a setup tool - Holds the stdin/stdout/stderr file descriptors for the container process (that's how
docker logsanddocker attachwork) - Relays signals like
SIGTERMto the application - Acts as the direct parent of your app process—so the app stays alive even if
containerdordockerdgoes away
That last point is the one most people don't expect.
Proof: Stop Docker, Container Keeps Running
sudo systemctl stop docker
ps -ef --forest | grep -E 'docker|containerd|nginx' | grep -v grep
dockerd is gone. containerd, containerd-shim, and nginx are still there. With live-restore: true, the shim holds the process alive independently of the engine.
Go further—kill containerd itself:
sudo kill $(pgrep -o containerd)
ps -ef --forest | grep -E 'containerd|nginx' | grep -v grep
The shim and Nginx survive. The shim is the direct parent; containerd dying is irrelevant to the running workload.
This is what makes zero-downtime Docker Engine upgrades possible: live-restore + the shim architecture means your app doesn't care what the daemons are doing.
Filesystem: No Hard Boundary Against Host Root
Containers provide process isolation—not a security boundary against a root user on the host. Here's proof:
NGINX_PID=$(pgrep -o nginx)
sudo cat /proc/$NGINX_PID/root/etc/nginx/nginx.conf
That's the container's Nginx config, read directly from the host via /proc. No volume mounts, no docker exec. The container's filesystem is just a view the kernel provides; /proc/<pid>/root exposes it to any sufficiently privileged host process.
Security implication: if the host is compromised at root level, assume all containers on that host are too.
What This Changes Operationally
A few places where this model pays off immediately:
-
Debugging "container still running after Docker restart": check for
containerd-shimand your app process on the host—they're justpsentries -
Port-mapping issues:
docker-proxyis a real process; it shows up inssorlsofoutput -
Engine upgrades:
live-restore: true+ a shim-aware mental model = no forced downtime -
Security reviews: treat host root access as full container access, because
/procmakes it so
The next time you type docker run, 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 runc 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.



Top comments (0)