containerd is a container runtime daemon: a long-running service that manages the complete lifecycle of containers on one machine — pulling and storing images, unpacking them into filesystems, handing execution to an OCI runtime, and supervising the result. It is not a developer tool and has no build system, no Compose and no opinion about workflow. It exposes a gRPC API and expects a client above it, which is exactly why Kubernetes uses it directly through the CRI plugin instead of going through a higher-level engine.
Internally containerd is a set of plugins behind that API, and the separation between them is what makes its behaviour predictable. The content store holds immutable, content-addressed blobs. The image service records manifests and tags pointing at that content. Snapshotters — overlayfs by default, with native, btrfs, zfs, devmapper and lazy-pulling options such as stargz and nydus — turn those layers into a mountable root filesystem. The task service starts processes by launching a shim, and every client operation happens inside a namespace, so the containers Kubernetes creates in the k8s.io namespace are invisible to a client working in the default one.
The shim is the design detail with the largest operational consequence. containerd does not parent container processes itself; containerd-shim-runc-v2 does, one per pod sandbox, and it calls runc to create the container and then stays alive holding the process. Because of that, containerd can be restarted or upgraded while workloads keep running. RuntimeClass and runtime handlers extend the same mechanism to alternative runtimes — gVisor, Kata Containers, crun — by pointing at a different shim.
Why this skill matters now
Kubernetes talks to containerd, not to a container engine. Since dockershim was removed from the kubelet, the runtime under almost every managed and self-managed cluster is containerd, and the tooling engineers reach for by habit no longer sees those containers at all. That gap turns routine node debugging into guesswork for teams who never learned the layer below the engine they started with.
The practical consequences show up constantly. Image pull failures and registry mirror configuration are resolved in containerd's config.toml and hosts.toml, not anywhere in Kubernetes. Cgroup driver mismatches between the kubelet and the runtime produce pods that fail in ways that look like scheduling problems. Disk pressure on a node is usually the content store and snapshots, and it is cleared by understanding leases and garbage collection rather than by deleting directories. Sandboxed runtimes for untrusted workloads are configured as runtime handlers with a RuntimeClass, one layer below anything Kubernetes documents in detail.
There is a performance dimension too. Image pull and unpack time is dominated by the snapshotter, and lazy-pulling snapshotters change node startup characteristics substantially for large images. Those are runtime decisions, made and diagnosed at the containerd layer.