containerd Content Store & Image Management
Understand how containerd stores and manages OCI images using content-addressable storage, metadata, and efficient distribution mechanisms.
The content store is the low-level storage system in containerd that manages all content blobs (image layers, manifests, configs) using content-addressable storage (CAS). Each piece of content is identified by its cryptographic hash (digest), ensuring integrity and deduplication.
This design allows containerd to efficiently store and retrieve image layers, share them across images, and verify content integrity. The content store works closely with the metadata store and snapshotter to provide a complete image management system.
containerd uses a content-addressable storage system where each content blob is stored under its digest (SHA256 hash). This provides several benefits:
- Deduplication: Identical layers are stored only once
- Integrity: Content can be verified by recalculating its hash
- Immutability: Content cannot be changed without changing its identifier
- Efficient distribution: Only new content needs to be transferred
# View content store (Linux)
ls /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256/
# Example content paths:
# /var/lib/containerd/io.containerd.content.v1.content/blobs/sha256/
# a1b2c3d4e5f6... (manifest)
# 1a2b3c4d5e6f... (layer)
# f6e5d4c3b2a1... (config)
# View content by digest
ctr content ls
# Show content info
ctr content info sha256:a1b2c3d4e5f6...
containerd fully supports the Open Container Initiative (OCI) image format. An OCI image consists of three main components:
- Manifest: JSON document describing the image layers and configuration
- Config: JSON document with image metadata (entrypoint, env vars, etc.)
- Layers: Tar archives containing the filesystem changes (tarballs)
# Inspect an OCI image manifest
ctr image ls
ctr image info docker.io/library/nginx:alpine
# View image configuration
ctr image ls -q | xargs ctr image info
# Example OCI manifest structure:
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 1234,
"digest": "sha256:abc123..."
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 5678,
"digest": "sha256:def456..."
}
]
}
When you pull an image, containerd follows a well-defined flow to fetch, verify, and store the image content:
# Pull an image with verbose output
ctr image pull --debug docker.io/library/nginx:alpine
# Pull with specific snapshotter
ctr image pull --snapshotter=overlayfs docker.io/library/nginx:alpine
The metadata store manages all high-level image metadata, including image names, tags, and references. It uses BoltDB (embedded key-value store) to persist metadata.
# Metadata store location
ls /var/lib/containerd/io.containerd.metadata.v1.bolt/
# meta.db - BoltDB database file
# View image metadata
ctr image ls
# View detailed image info
ctr image info docker.io/library/nginx:alpine
# The metadata store tracks:
# - Image names and tags
# - Image digests (manifest and config)
# - Content references
# - Labels and annotations
# List all content
ctr content ls
# Show content details
ctr content info sha256:a1b2c3d4e5f6...
# Delete content
ctr content rm sha256:a1b2c3d4e5f6...
# Verify content integrity
ctr content verify sha256:a1b2c3d4e5f6...
# Export content to file
ctr content export content.tar
# Import content from file
ctr content import content.tar
# List images (high-level view)
ctr image ls
# Remove image
ctr image rm docker.io/library/nginx:alpine
# Pull image from Docker Hub
ctr image pull docker.io/library/nginx:alpine
# Pull from private registry
ctr image pull myregistry.azurecr.io/myapp:latest
# Push image to registry (must be tagged)
ctr image tag docker.io/library/nginx:alpine myregistry/nginx:alpine
ctr image push myregistry/nginx:alpine
# Push with specific platform
ctr image push --platform linux/amd64 myregistry/nginx:alpine
# Use nerdctl for Docker-like experience
nerdctl pull nginx:alpine
nerdctl push myregistry/nginx:alpine
# Export image to tar file
ctr image export nginx.tar docker.io/library/nginx:alpine
# Or with nerdctl
nerdctl save -o nginx.tar nginx:alpine
# Import image from tar file
ctr image import nginx.tar
# Or with nerdctl
nerdctl load -i nginx.tar
# Export multiple images
ctr image export images.tar docker.io/library/nginx:alpine docker.io/library/redis:alpine
You can configure containerd to use specific registry mirrors, authentication, and TLS settings.
# Registry configuration in config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
# Mirror configuration
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://mirror.gcr.io", "https://registry-1.docker.io"]
# Host configuration
[plugins."io.containerd.grpc.v1.cri".registry.configs."myregistry.azurecr.io".auth]
username = "myuser"
password = "mypassword"
# TLS configuration
[plugins."io.containerd.grpc.v1.cri".registry.configs."myregistry.azurecr.io".tls]
insecure_skip_verify = true # Not recommended for production
# Show image layers
ctr image ls
ctr image info docker.io/library/nginx:alpine | jq '.layers'
# Pull specific layer
ctr content fetch docker.io/library/nginx:alpine@sha256:abc123...
# View layer size breakdown
ctr image ls | awk '{print $1, $2}' | while read name tag; do
ctr image info "$name:$tag" | grep -A 5 layers
done
# Clean up unused layers (by removing unused images)
ctr image rm $(ctr image ls -q)
# Prune unused content
ctr content prune
The content store is the foundation of containerd's image management system. Understanding it helps you manage container images efficiently.