Containerd Image Store, Snapshotters, Runtime Internals, OCI Runtime Specs, and Docker Engine Evolution: Concepts, Architecture, and Mental Model
Trace Docker image and container operations through the Engine-managed containerd content store, snapshotters, tasks, shims, OCI runtime bundles, and runc without treating Docker-managed internals as a second control plane.
Learning objectives
- Explain how a Docker image operation becomes containerd content/metadata, a prepared snapshot, a task/shim, an OCI runtime bundle, and finally a process.
- Distinguish immutable OCI content identity from mutable local metadata, unpacked snapshots, container writable state, and process identity.
- Identify whether a daemon uses the Engine 29 containerd image store or a classic graph-driver architecture without guessing from old tutorials.
-
Explain why Docker owns its managed containerd namespace/state and
why direct
ctrmutation is not a supported second control plane. - Record version and migration evidence before changing storage/runtime architecture.
1. The practical problem: old storage diagrams no longer describe every Docker host
Earlier chapters taught image indexes, manifests, configs, layers, containers, filesystems, and runtime processes from the Docker user’s point of view. Engine 29 changes an important implementation default: on a fresh Engine 29 installation, Docker uses the containerd image store by default. A daemon upgraded from an older release can continue using the classic graph-driver architecture until you deliberately migrate or switch.
That means two healthy Engine 29 hosts can expose the same Docker
CLI while storing image/container root filesystems differently. An
operator who assumes every host is still overlay2 may
search the wrong directory, misread docker info, or
make a dangerous downgrade plan. The correct starting point is
always evidence from the running daemon.
2. Mental model: content is not a snapshot, and a snapshot is not a process
flowchart TD
A[Docker CLI / API request] --> B[dockerd owns desired state]
B --> C[Engine-managed containerd image metadata + content]
C --> D[Snapshotter unpacks / prepares rootfs]
D --> E[containerd container + task]
E --> F[containerd-shim-runc-v2]
F --> G[OCI bundle + config.json]
G --> H[runc creates process via host kernel]
H --> I[Container process + writable snapshot]
C --> J[OCI index / manifest / config / layer digests]
The CLI first talks to the selected Docker Engine endpoint.
dockerd remains the owner of Docker objects and policy.
When the containerd image store is active, Engine uses containerd’s
content and image metadata services for OCI blobs and platform
metadata. A snapshotter prepares filesystem views
from image layers. Starting a container creates runtime state and a
task; containerd launches a shim, which coordinates
the OCI runtime such as runc. The OCI runtime consumes
a bundle containing a root filesystem and config.json,
then asks the host kernel to create the process with the requested
namespaces, cgroups, mounts, capabilities, and other settings.
These arrows matter during diagnosis. A registry digest can be correct while unpacking fails. A snapshot can exist while no task is running. A task can exit while image content remains. Recreating a process should not change an immutable image manifest digest.
3. The five identities you must keep separate
| Identity/state | Typical evidence | Do not confuse it with |
|---|---|---|
| OCI content digest |
sha256:... descriptor for
index/manifest/config/layer
|
Snapshot key, local image ID, filesystem inode, container ID |
| Docker image reference | alpine:3.22.1 or repo@digest |
The bytes until the reference is resolved |
| Snapshot/rootfs state | snapshotter metadata + unpacked filesystem | The registry manifest digest |
| Container object/task | Docker container ID, runtime/task state | The image object or shim PID |
| Process identity | host PID / container PID namespace view | The Docker container ID or image ID |
4. Engine 29 image-store modes
The current Docker storage documentation describes two broad architectures you can still encounter:
| Architecture | Typical current origin | What to expect |
|---|---|---|
| containerd image store | Fresh Engine 29+ installs; Docker Desktop uses containerd by default | Snapshotters, native multi-platform image/index storage, attestations, compressed content plus unpacked snapshots. |
| Classic graph-driver store | Engine upgraded from older releases unless switched/migrated; compatibility cases |
Classic drivers such as overlay2; local store
has older image-index/attestation limitations.
|
Engine 29’s containerd image store is currently unavailable with
daemon userns-remap. That is a compatibility
constraint, not a reason to disable user namespaces casually.
Chapter 27’s identity/security requirements may be more important
than a storage feature on a particular host.
5. Content store and OCI image objects
The OCI Image Specification defines content-addressable descriptors. An image index can point to platform-specific manifests; each manifest points to an image configuration and filesystem layer blobs. The digest authenticates bytes at that descriptor boundary. containerd keeps those content blobs and image metadata so Docker can resolve and reuse them.
The content store commonly retains compressed layer blobs as received from a registry. To run a container, the selected platform’s layers must also be unpacked into a form the snapshotter can mount. Docker documents that this dual representation can consume more disk space than the classic store for the same image. Therefore “registry size,” “content-store bytes,” “unpacked snapshot bytes,” and “Docker-reported reclaimable space” are related but not interchangeable metrics.
6. Snapshotters: root filesystem preparation, not image identity
A containerd snapshotter manages filesystem snapshots used to unpack
images and provide writable container root filesystems. Docker
Engine’s default containerd snapshotter is overlayfs.
The name deliberately differs from Docker’s classic
overlay2 storage driver even though both rely on Linux
OverlayFS concepts.
containerd supports other snapshotters, including filesystem-specific and remote/lazy-pull designs. A snapshotter choice affects unpack behavior, kernel/filesystem prerequisites, startup latency, caching, storage cost, and operational support. It does not redefine the OCI manifest digest. Two systems can materialize the same image digest through different supported snapshotters.
7. Container, task, shim, and runc
containerd distinguishes a persistent container metadata object from
a running task. Docker maps its own higher-level
container lifecycle onto these lower-level concepts. On Linux, a
containerd-shim-runc-v2 process normally remains
associated with the running task. The shim isolates container
I/O/lifecycle from the long-lived containerd process and allows
containers to keep running through some daemon-level transitions.
runc is an OCI runtime implementation. It consumes the
OCI Runtime Specification configuration and creates the actual
process. It is not an image registry, image builder, Docker daemon,
or long-lived orchestration API.
8. OCI runtime bundle: the last portable contract before execution
OCI Runtime Spec 1.3.0 defines the runtime configuration and
lifecycle. A runtime bundle contains a root filesystem and a
config.json. That JSON describes process
arguments/environment, namespaces, mounts, Linux resources,
capabilities, seccomp-related runtime settings, and other
platform-specific state. Docker does not ask you to hand-author this
bundle for normal use; Engine and containerd derive it from Docker’s
container configuration.
9. Read-only inspection first
docker context show
docker version
docker info
# Engine 29 containerd image-store evidence when active.
docker info --format '{{json .DriverStatus}}'
# Image identity remains Docker/OCI evidence.
docker image ls --digests
docker buildx imagetools inspect alpine:3.22.1
# Current runtime selected by Engine.
docker info --format 'default-runtime={{.DefaultRuntime}} runtimes={{json .Runtimes}}'
Do not assume a missing field means a broken daemon. Docker CLI
templates and server fields evolve. Preserve the full
docker info output alongside any compact template so
future readers can interpret the evidence with the recorded Engine
version.
10. Data-root reality on current Engine
On Linux, Docker’s general daemon data remains under
/var/lib/docker by default. With the containerd image
store, Docker documents image content and container snapshots under
/var/lib/containerd. Setting Docker’s
data-root alone does not move the containerd
snapshot/content root. This distinction matters for disk-capacity
planning, backup strategy, encrypted partitions, and incident
response.
These paths are implementation storage, not an operator API. Do not “repair” Docker by deleting blobs, editing containerd metadata, or copying snapshot directories behind the daemon’s back.
11. Engine evolution: managed versus embedded containerd
Engine 29.7 introduced an
experimental embedded-containerd mode. The default
remains a separately managed containerd process; embedded mode
places the containerd server inside dockerd while task
shims remain separate. Docker explicitly labels this experimental.
It changes component topology and IPC cost, not the ownership rule:
Engine still owns that state.
As of the 2026-09-22 lesson baseline, Engine 29.8.1 is current. Its release notes list containerd 2.3.5 for static-binary packaging; Engine 29.8.0 updated BuildKit to 0.33.0 and runc to 1.5.1. Distribution packages and Docker Desktop bundles can differ, so actual command evidence wins over a course table.
12. What to carry forward
The useful mental model is layered ownership: Docker reference/digest identity → Engine-managed containerd content → unpacked snapshot → container/task/shim → OCI runtime configuration → kernel process. Every layer has different identifiers, failure modes, and cleanup semantics. In the next lesson, you will trace those layers on a disposable workload without mutating internal containerd state.
Knowledge check
A registry manifest digest and a containerd snapshot key are the same thing. True or false?
False. A digest identifies immutable OCI content; a snapshot key identifies local unpacked filesystem state managed by a snapshotter.
Why can two Engine 29 hosts legitimately show different storage architectures?
Fresh Engine 29 installations default to the containerd image store, while upgraded daemons can remain on the classic graph-driver store until switched or migrated.
What is the operational role of runc in this chain?
runc is the OCI runtime that creates/starts the process from an OCI runtime bundle/configuration; it is not the Docker control plane or image store.
Why should operators not delete Docker-managed objects using ctr?
Docker owns the managed containerd state. Out-of-band mutation can make Docker metadata and containerd state diverge and can destroy recovery evidence.
What does io.containerd.snapshotter.v1 in
DriverStatus tell you?
It is evidence that Engine is using the containerd snapshotter-backed image-store path rather than the classic graph-driver architecture.
Official references and version notes
2026-09-22. Engine 29.8.1 is current. Docker’s release notes list containerd 2.3.5 for static-binary packaging; Engine 29.8.0 updated BuildKit to 0.33.0 and runc to 1.5.1. OCI Runtime Spec 1.3.0 and OCI Image Spec 1.1.1 are the current published spec baselines used here. Record actual installed component versions because Docker Desktop and distribution packaging can differ.
- Docker Docs — containerd image store with Docker Engine — Engine 29 fresh-install defaults, snapshotters, disk layout, switching, and experimental migration guidance.
- Docker Docs — Storage drivers — distinction between classic graph drivers and the Engine 29 containerd image store.
- Docker Docs — Select a storage driver — current storage-backend matrix and platform notes.
-
Docker Docs — Docker daemon configuration overview
—
/var/lib/dockerversus/var/lib/containerdand data-root implications. - Docker Docs — Run containerd in the Docker daemon — Engine 29.7+ experimental embedded-containerd mode and debugging endpoint warning.
- Docker Engine 29 release notes — Engine 29.8.1 baseline, containerd 2.3.5 static-binary packaging, BuildKit 0.33.0 and runc 1.5.1 updates.
- containerd 2.3 — Features — namespaces, images, root filesystems, snapshots, containers, tasks, and OCI runtime integration.
-
containerd — Snapshotters
— core snapshotter behavior and the
overlayfsnaming used by containerd. - OCI Image Specification 1.1.1 — image indexes, manifests, configs, layers, and content-addressable descriptors.
-
OCI Runtime Specification 1.3.0
— runtime bundle,
config.json, execution environment, and lifecycle model.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.