Containerd Explained: The Lean Container Runtime Powering Kubernetes (Not an Orchestrator)
If you’re building on Kubernetes or modern CI/CD, you’re already touching containerd—even if you never type its name. Teams often mistake it for “another Docker” or even an orchestrator. It’s neither. containerd is a high-performance, OCI-compliant container runtime that sits under your tools, doing the heavy lifting: fetching images, managing snapshots, starting/stopping containers, and exposing clean APIs that higher layers (like Kubernetes) use.
What containerd is (and isn’t)
- Is: A CNCF-graduated, production-grade container runtime with a stable API and gRPC services for images, snapshots, tasks, events.
- Is not: A scheduler or orchestrator. Kubernetes, Nomad, or Swarm do the orchestration; containerd runs the containers.
- Works with:
- Kubernetes via CRI (kubelet ↔ CRI plugin ↔ containerd ↔ runc)
- nerdctl (a Docker-like CLI for containerd)
- runc as the default OCI runtime (pluggable)
Architecture at a glance
Think of a restaurant:
- Kubernetes kubelet = maître d’ taking orders (Pods)
- containerd = kitchen manager coordinating stations (images, snapshots, tasks)
- runc = the cook that actually executes the recipe (creates Linux processes in namespaces/cgroups)
- Snapshotters (overlayfs, btrfs, zfs) = pantry systems handling copy-on-write layers
- Content store = walk-in fridge for OCI artifacts (images, layers)
Core components:
- Content: Stores blobs (layers, configs) with CAS semantics.
- Images: References to content with metadata.
- Snapshotters: Filesystem layer mounts (overlayfs is most common).
- Tasks: Running containers (via runc or another OCI runtime).
- CRIs: Kubernetes integration (containerd’s CRI plugin).
- Plugins: Extensible points for runtime, snapshotters, content, metrics.
When to choose containerd
- You want Kubernetes without the Docker shim (native CRI, fewer moving parts).
- You need performance and predictable behavior for large clusters.
- You prefer a minimalist, composable runtime you can script and observe.
- You’re building edge or appliance workloads with tight resource budgets.
Quick comparison
| Capability / Tool | containerd | Docker Engine | CRI-O | runc | Podman |
|---|---|---|---|---|---|
| Role | Runtime (API + services) | Engine (CLI + API + runtime) | Runtime (K8s-focused) | Low-level OCI executor | CLI/daemonless engine |
| K8s CRI | Yes (built-in) | Deprecated for K8s | Yes | No | Via crun/other runtimes + CRI-O |
| CLI included | No (use ctr/nerdctl) | Yes (docker) | No | No | Yes (podman) |
| Scope | Lean, composable | Full developer UX | K8s-only minimalism | Start/kill containers | Dev + rootless focus |
| Typical use | Production under K8s | Dev + legacy prod | Strict K8s shops | Used by containerd | Dev/ops alt to Docker |
Hands-on: containerd basics
Install & sanity check
# systemd-based
sudo systemctl enable --now containerd
ctr version
Pull and run with ctr (low-level)
sudo ctr images pull docker.io/library/nginx:1.27
sudo ctr run --rm --net-host docker.io/library/nginx:1.27 web1
Use nerdctl (Docker-like UX)
# great for day-to-day
sudo nerdctl run -d --name web -p 8080:80 nginx:1.27
sudo nerdctl ps
sudo nerdctl logs -f web
Configure containerd (snippet)
/etc/containerd/config.toml
version = 2
[plugins.”io.containerd.grpc.v1.cri”.containerd]
snapshotter = “overlayfs” disable_snapshot_annotations = false
[plugins.”io.containerd.grpc.v1.cri”.registry.mirrors.”docker.io”]
endpoint = [“https://registry-1.docker.io”]
sudo systemctl restart containerd
Kubernetes with containerd (concept)
- kubelet talks CRI → containerd’s CRI plugin → containerd spawns tasks via runc.
- No Docker shim, fewer layers to debug.
Observability & debugging
- List images/containers:
ctr images ls,ctr tasks ls,nerdctl ps - Events stream:
ctr events - Runtime state:
sudo crictl ps -a(if CRI tools installed) - Logs:
journalctl -u containerd -f - Inspect image content:
ctr content ls,ctr images info <ref>
Best practices
- Prefer
nerdctlfor everyday UX; fall back toctrfor internals. - Pin base images (e.g.,
nginx:1.27.2) to avoid surprise upgrades. - Harden the node: enable AppArmor/SELinux; keep runc/containerd patched.
- Choose the right snapshotter:
overlayfs: default, fast on most distrosbtrfs/zfs: advanced features, but operational trade-offs
- Tune cgroups for noisy neighbors (CPU quotas, memory limits).
- Use private registries with auth & content trust where possible.
- Rootless where appropriate (nerdctl + rootless containerd) for security.
Common pitfalls (and fixes)
- “containerd isn’t an orchestrator.” True—pair it with Kubernetes or Nomad to schedule.
- Image GC surprises: Layers reclaimed if unreferenced. Set sensible GC thresholds and keep pull-through caches warm.
- DNS/network quirks in Pods: Usually a CNI plugin issue, not containerd. Check CNI config and kubelet resolv.conf options.
- Overlayfs on exotic filesystems: Validate kernel support and xattr options; consider alternate snapshotters if needed.
- Mixed CLIs confusion: Don’t mix
dockerandnerdctlon the same node for the same workloads; state lives in different runtimes.
Real-world pattern: high-throughput API on K8s
- Kubernetes schedules 1,000+ Pods across nodes.
- kubelet on each node asks containerd (via CRI) to create Pods/containers.
- containerd pulls images once per node, mounts layers via overlayfs, and launches tasks via runc.
- DaemonSets push node-exporter and containerd metrics to Prometheus.
- Rolling updates: new image → containerd streams layers with content-store dedupe → fast rollout with minimal bandwidth.
Impact: faster start times, fewer moving parts, clean debugging (kubelet → CRI → containerd → runc).
Security checklist
- Keep containerd, runc, and the kernel up to date (watch CVEs).
- Enforce seccomp/AppArmor/SELinux profiles.
- Use read-only root filesystems and non-root users in images.
- Restrict privileged and hostPath usage in K8s.
- Sign/verify images (cosign), and enable image policy in admission control.
Internal link ideas (for your doc site/blog)
- “Kubernetes CRI Deep Dive: kubelet ↔ containerd”
- “OCI Artifacts & Content Store 101”
- “Choosing a Snapshotter: overlayfs vs btrfs vs zfs”
- “Rootless Containers with nerdctl”
- “runc vs crun: Performance and Security”
Conclusion & takeaways
- containerd is the runtime backbone, not the scheduler.
- It delivers speed, simplicity, and reliability at scale.
- Pair it with Kubernetes, use
nerdctlfor dev ergonomics, harden the node, and monitor the runtime. - Get the layers right (content, snapshots, tasks), and your platform stays boring—in a good way.
Image prompt (for AI tools)
“A clean, modern diagram of the container runtime stack showing kubelet → CRI → containerd (content, images, snapshots, tasks) → runc → Linux namespaces/cgroups; include registry and snapshotter; minimalistic, high-contrast, 3D isometric style.”
Tags
#Containerd #Kubernetes #OCI #DevOps #CloudNative #Containers #Runtime #Linux
Containerd, Kubernetes, OCI, DevOps, Cloud Native, Containers, Runtime, Linux




