containerd/containerd
Metadata store
A bbolt-backed key/value store at plugins/metadata/ that holds everything that isn't a blob: images, containers, leases, sandboxes, namespaces, snapshot metadata, and label sidecars on content blobs.
Purpose
- Provide ACID-ish updates (bbolt is single-writer) for objects that need to be queried by id, by labels, or by reference.
- Wrap the content store and snapshotters so that labels on those blobs/snapshots are tracked here even though their payloads live elsewhere.
- Be the canonical source of truth for the GC reference graph.
Directory layout
core/metadata/
├── images.go # image manifest store
├── containers.go # container CRUD
├── leases.go # lease manager wrapper
├── sandboxes.go # sandbox metadata
├── snapshotter.go # snapshotter wrapper that stores labels and indexes
├── content.go # content store wrapper
├── namespaces.go
├── gc.go # GC reference walker (the shaving brush)
└── ...
plugins/metadata/
└── ... # plugin registration; opens a single bbolt db fileBbolt schema
The metadata store packs everything into a single meta.db file under the plugin's root directory (e.g. /var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db). Top-level structure:
v1/ (schema version)
└── <namespace>/
├── containers/<id>/ (labels, image, snapshotter, runtime, spec, ...)
├── images/<name>/ (target descriptor + labels)
├── leases/<id>/ (resources owned by the lease)
├── sandboxes/<id>/ (sandbox state)
├── snapshots/<name>/ (snapshotter labels indexed by snapshotter id)
└── content/blob/<digest>/ (blob labels)The schema version is bumped on incompatible changes; the migration logic lives in plugins/metadata/migrations.go.
Key abstractions
| Type | File | Purpose |
|---|---|---|
metadata.DB |
plugins/metadata/db.go |
The bbolt database wrapper, exposes Init, View, Update, GarbageCollect |
metadata.imageStore |
core/metadata/images.go |
Implements images.Store over bbolt |
metadata.containerStore |
core/metadata/containers.go |
Implements containers.Store |
metadata.snapshotter |
core/metadata/snapshotter.go |
Wraps a real snapshotter and stores labels in bbolt |
metadata.contentStore |
core/metadata/content.go |
Wraps the local content store and stores labels in bbolt |
metadata.GCRoots |
core/metadata/gc.go |
Iterator over GC roots; consumed by the GC scheduler |
How writes propagate
Updates to an image, container, or lease are wrapped in a single bbolt transaction. If the request also touches the content store (e.g. a label update on a blob), the update goes through the metadata.contentStore wrapper — the wrapper reads/writes labels in the bbolt transaction and delegates the content-side update to the underlying content store. This keeps the two views consistent.
Snapshotter integration
metadata.snapshotter wraps an underlying snapshotter and stores per-snapshot labels and namespacing metadata in bbolt:
- The bbolt key holds the labels and the namespace.
- The underlying snapshotter holds the actual snapshot data.
This allows the metadata store to participate in GC: it knows which snapshots are referenced by which containers and which leases.
GC integration
core/metadata/gc.go walks the bbolt buckets to enumerate GC roots:
- Containers' image and snapshot references
- Leased resources
- Image manifest descendants (manifests → layers → blobs)
- Sandbox-related blobs and snapshots
The walker emits gc.Node records that the scheduler in plugins/gc/ consumes. See GC.
Concurrency
bbolt allows a single writer at a time. The metadata package serializes writes through bbolt's Update transaction. Reads use View and are non-blocking against each other. Long-running reads that span many keys (typically GC scans) hold a snapshot view and don't block writers.
Entry points for modification
- Add a new object type: define a bucket layout under
core/metadata/, implement aStoreinterface, and add a migration step inplugins/metadata/migrations.go. - Add a new GC reference: extend the
referencesfunction incore/metadata/gc.go. Be careful — missing a reference here causes silent data loss when the GC runs. - Schema upgrade: add a
migrationentry that runs once on first open of the new version.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.