The Role of containerd
What does it mean to run a container? Many explanations stop at "container = isolated process," but this is only half true.
Container execution requires the following to be true simultaneously:
- Which process to run
- On which filesystem to run it
- To which namespace it should belong
- Which resource limit cgroup to apply
- Who will remember and manage this state
This is where a core problem arises.
The act of launching a single process and managing its entire lifecycle are completely different problems.
runc Limitations #
The limitation of runc is that it executes but does not manage.
runc is strictly a momentary execution tool; it performs clone + namespace + cgroup setup according to the OCI spec, then calls execve, and that's it.
From runc's perspective, it doesn't care what happens to a container after it's created; it doesn't know if it died, needs restarting, where the image came from, or its relationship with other containers.
This means runc only has actions, not state. At this point, the necessity of containerd emerges.
containerd #
containerd does not run containers; instead, it manages the state transitions of containers.
Containers always have a state.
- Image state (pull/unpack)
- RootFS state (snapshot created)
- Container definition state (exists/deleted)
- Task state (running/terminated)
- Resource state (cgroup configuration)
- Event state (start, termination, failure)
A system is needed that persistently records all these states, recovers them after a restart, and makes them observable to external entities (like kubelet). That is containerd.
It's important not to misunderstand here: saying that containerd launches containers by executing runc is superficially correct but conceptually wrong.
What containerd does:
- Stores the state that a container exists
- Defines the snapshot of this container's RootFS
- Selects runc as the runtime to execute this container and reflects the execution result as state
- Publishes an event when terminated
As described above, containerd is a lifecycle manager for the container object, not a process manager. This means runc and containerd are independent.
Boundary with kubelet #
kubelet does not answer how the container's RootFS was created, where the image layers are, how duplicates of the same image were removed, or which containers survived a node restart.
It merely maintains the instruction to run a pod in a certain state. It is a pod management agent for maintaining the desired state.
containerd receives requests from kubelet to manage the pod's desired state, manages them with an immutable image model, resolves them from a filesystem perspective, and maps them to a kernel resource model.
Therefore, containerd is not a Kubernetes extension but a runtime layer that Kubernetes must depend on.
State Machine Perspective #
A container is a state machine.
Image Pulled
→ Snapshot Created
→ Container Created
→ Task Running
→ Task Exited
→ Garbage Collected
containerd is a system that accurately guarantees these state transitions.
Immutable Object Model (Content Address) #
Images are never modified; they are referenced by digest.
This enables deduplication of identical layers, reproducible execution, and stable caching.
This is not just a simple implementation but a philosophy for container reproducibility and stability.
Separation of Concerns #
containerd intentionally does not handle many things.
It doesn't concern itself with builds, scheduling, or network policies because a state management system must always prioritize predictability and simplicity.
To summarize in one sentence, containerd is not a program that runs containers, but rather
a state manager that consistently projects the abstract container object onto kernel resources.