containerd/containerd
Architecture
containerd runs as a daemon process that loads a graph of plugins and exposes their services over gRPC and ttrpc sockets. Containers themselves are not run inside the daemon; each container is launched as a separate shim process which talks to the daemon over ttrpc and to the OCI runtime (runc on Linux, runhcs on Windows) on the other side.
Process layout
graph LR
Client[ctr / nerdctl / Docker / kubelet]
Daemon[containerd daemon]
Shim[containerd-shim-runc-v2]
Runc[runc]
Container((container))
Client -->|gRPC / CRI| Daemon
Daemon -->|spawn + ttrpc| Shim
Shim -->|exec| Runc
Runc --> Container
Shim -->|events| Daemon
Daemon -->|events| Client- The daemon (
cmd/containerd/main.go) is a small bootstrap that calls intocmd/containerd/commandto build the urfave/cli app, thencmd/containerd/serverto construct aServerfrom the loaded plugins. - A shim is launched per container (or per pod sandbox) by the runtime plugin. The shim outlives the daemon — if
containerdis restarted, running containers keep running and the shim re-attaches. - Clients can be the in-process Go client at
client/, thectrCLI, the kubelet via the CRI plugin, or any third-party gRPC client.
See containerd binary, containerd-shim-runc-v2, and runtime v2 system for the per-binary details.
Plugin model
Almost everything inside the daemon is a plugin. At startup, LoadPlugins in cmd/containerd/server/server.go asks the global plugin registry for the topologically-sorted graph of registered plugins, applies disabled-plugin filters from the config, then initializes each plugin in dependency order.
Each plugin is identified by a (Type, ID) pair where the type is one of the constants in plugins/types.go:
| Plugin type | URI | Examples |
|---|---|---|
RuntimePluginV2 |
io.containerd.runtime.v2 |
runc shim launcher in core/runtime/v2 |
SnapshotPlugin |
io.containerd.snapshotter.v1 |
overlayfs, btrfs, devmapper, native, blockfile, erofs, windows, lcow |
ContentPlugin |
io.containerd.content.v1 |
local content store at plugins/content/local |
MetadataPlugin |
io.containerd.metadata.v1 |
bbolt-backed metadata store at plugins/metadata |
DiffPlugin |
io.containerd.differ.v1 |
walking, erofs, windows, lcow differs in plugins/diff/* |
GRPCPlugin |
io.containerd.grpc.v1 |
each gRPC service registered under plugins/services/* |
TTRPCPlugin |
io.containerd.ttrpc.v1 |
ttrpc shim entry points |
ServerPlugin |
io.containerd.server.v1 |
plugins/server/grpc, plugins/server/ttrpc, plugins/server/debug, plugins/server/metrics |
SandboxControllerPlugin |
io.containerd.sandbox.controller.v1 |
plugins/sandbox, internal/cri/server/podsandbox |
CRIServicePlugin |
io.containerd.cri.v1 |
CRI runtime/image services in internal/cri |
NRIApiPlugin |
io.containerd.nri.v1 |
Node Resource Interface adapter in plugins/nri |
TransferPlugin |
io.containerd.transfer.v1 |
image pull/push transfer service in plugins/transfer |
EventPlugin |
io.containerd.event.v1 |
event exchange in plugins/events |
GCPlugin |
io.containerd.gc.v1 |
garbage collection scheduler in plugins/gc |
LeasePlugin |
io.containerd.lease.v1 |
lease manager in plugins/leases |
StreamingPlugin |
io.containerd.streaming.v1 |
streaming manager in plugins/streaming |
WarningPlugin |
io.containerd.warning.v1 |
deprecation/warning service |
MountManagerPlugin / MountHandlerPlugin |
io.containerd.mount-manager.v1 / io.containerd.mount-handler.v1 |
plugins/mount, plugins/mount/erofs, plugins/mount/fsview |
ImageVerifierPlugin |
io.containerd.image-verifier.v1 |
binary image verifier in plugins/imageverifier |
Built-ins are wired into the daemon by blank-importing them from cmd/containerd/builtins/builtins.go. Proxy plugins are configured by the operator in the daemon TOML and connect to an out-of-process gRPC server speaking the corresponding service API; see proxy plugins.
Request flow: pulling and running an image
sequenceDiagram
autonumber
participant Client
participant Daemon as containerd daemon
participant Transfer as Transfer service
participant Content as Content store
participant Meta as Metadata (bbolt)
participant Snap as Snapshotter
participant Runtime as Runtime v2
participant Shim
participant Runc
Client->>Daemon: Transfer(Pull{ref})
Daemon->>Transfer: stream pull
Transfer->>Content: write blobs
Transfer->>Meta: record image manifest
Client->>Daemon: NewContainer(image, snapshotter)
Daemon->>Snap: Prepare(rootfs)
Daemon->>Meta: store container record
Client->>Daemon: Tasks.Create(container)
Daemon->>Runtime: Create(opts, mounts)
Runtime->>Shim: spawn binary
Shim->>Runc: create + start
Runc-->>Shim: container running
Shim-->>Daemon: TaskCreate / TaskStart events
Daemon-->>Client: events streamThe same flow is driven by the kubelet through the CRI plugin in internal/cri, but the container creation is wrapped inside a sandbox.
Storage layout on disk
containerd separates persistent state from volatile state:
--root(default/var/lib/containerd) holds long-lived data: the bbolt metadata DB, the local content store, snapshotter data, etc. Each plugin gets its own subdirectory (io.containerd.<type>.<version>.<id>/).--state(default/run/containerd) holds runtime data that should disappear on reboot: shim sockets, fifos, task state.
Both paths are created by CreateTopLevelDirectories in cmd/containerd/server/server.go with mode 0700 (root) and 0711 (state).
Sockets
By default the daemon listens on:
unix:///run/containerd/containerd.sock— gRPC API for clientsunix:///run/containerd/containerd.sock.ttrpc— ttrpc API for shimsunix:///run/containerd/debug.sock— debug/introspection (when enabled)
The defaults/ package centralizes these constants per-OS.
Cross-cutting concerns
- Namespaces isolate containers/images/leases on the same daemon. Every gRPC call carries a namespace via metadata; see
pkg/namespacesand namespaces glossary. - Leases prevent garbage collection of in-progress objects (a downloaded blob, an active snapshot). See
plugins/leasesandcore/leases. - Garbage collection runs in
plugins/gc, scheduled against the metadata store; objects without a lease and without inbound references are deleted. - Tracing is wired through OpenTelemetry. The default tracing processor lives in
plugins/server/internaland the SDK is configured bytracing.gobuiltins. - Metrics are Prometheus-compatible, exposed by
plugins/server/metrics.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.