Chapter 31Lesson 01~150 minutes

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.

containerdSnapshottersOCI runtimeruncEngine 29

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 ctr mutation 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.

Ownership rule for this chapter. Docker Engine owns the containerd instance/namespace it manages. Use Docker APIs/CLI for normal lifecycle operations. Low-level containerd tools can be useful for read-only debugging, but must not be used to delete, retag, unpack, import, start, or otherwise mutate Docker-managed state.

2. Mental model: content is not a snapshot, and a snapshot is not a process

Causal model: Docker intent becomes content, rootfs state, task/shim, OCI runtime configuration, and 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.

Diagnostic use. Think of the OCI spec as the semantic boundary between “Docker/containerd decided what should run” and “the low-level runtime/kernel created it.” That boundary helps classify failures without turning internal files into a supported editing interface.

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?

Why can two Engine 29 hosts legitimately show different storage architectures?

What is the operational role of runc in this chain?

Why should operators not delete Docker-managed objects using ctr?

What does io.containerd.snapshotter.v1 in DriverStatus tell you?

Next lesson

Next: Containerd Image Store, Snapshotters, Runtime Internals, OCI Runtime Specs, and Docker Engine Evolution: Guided Hands-On Workflow and Core Operations

Continue with the next lesson in the course sequence and carry forward the evidence-first Docker operating model.

Official references and version notes

Verified baseline date:

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.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.