containerd/containerd · critical
failed to initialize database: %w
Error message
failed to initialize database: %w
What it means
NewPoolMetadata wraps any failure from ensureDatabaseInitialized, which opens/creates the bolt DB and creates the required buckets (devices, deviceids, poolmeta) for the devmapper metadata store. It means the persistent metadata database could not be set up, so the snapshotter pool cannot track devices. The underlying bolt error (I/O error, corrupt file, permission denied) is preserved via %w.
Source
Thrown at plugins/snapshots/devmapper/metadata.go:74
ErrAlreadyExists = errdefs.ErrAlreadyExists
)
// PoolMetadata keeps device info for the given thin-pool device, generates next available device ids,
// and tracks devmapper transaction numbers
type PoolMetadata struct {
db *bolt.DB
}
// NewPoolMetadata creates new or opens existing pool metadata database
func NewPoolMetadata(dbfile string) (*PoolMetadata, error) {
db, err := bolt.Open(dbfile, 0600, nil)
if err != nil {
return nil, err
}
metadata := &PoolMetadata{db: db}
if err := metadata.ensureDatabaseInitialized(); err != nil {
return nil, fmt.Errorf("failed to initialize database: %w", err)
}
return metadata, nil
}
// ensureDatabaseInitialized creates buckets required for metadata store in order
// to avoid bucket existence checks across the code
func (m *PoolMetadata) ensureDatabaseInitialized() error {
return m.db.Update(func(tx *bolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists(devicesBucketName); err != nil {
return err
}
if _, err := tx.CreateBucketIfNotExists(deviceIDBucketName); err != nil {
return err
}
return nilView on GitHub (pinned to 4246446a2b)
Solutions
- Check permissions/ownership of the devmapper snapshotter root path and its metadata.db file; ensure the containerd user has read-write access.
- Confirm the filesystem holding the DB is writable and not full (df, mount | grep ro).
- If the DB is corrupt or from an incompatible version, stop containerd, back up and remove metadata.db, and let the snapshotter recreate it (devices will need reactivation).
- Ensure only one containerd instance uses this pool; check for stale lock holders.
Example fix
// before snapshotter root: /var/lib/containerd/io.containerd.snapshotter.v1.devmapper (owned by root, containerd runs as user) // after chown -R containerd:containerd /var/lib/containerd/io.containerd.snapshotter.v1.devmapper
Defensive patterns
Strategy: validation
Validate before calling
dbPath := filepath.Join(rootDir, "metadata.db")
if fi, err := os.Stat(filepath.Dir(dbPath)); err != nil || !fi.IsDir() {
return fmt.Errorf("snapshotter root missing: %s", rootDir)
}
if f, err := os.OpenFile(dbPath, os.O_RDWR, 0o600); err != nil {
return fmt.Errorf("metadata.db not writable: %w", err)
} else {
f.Close()
} Try / catch
md, err := devmapper.NewPoolMetadata(ctx, dbPath)
if err != nil {
return fmt.Errorf("check metadata.db path permissions/disk and that no other containerd holds it: %w", err)
} Prevention
- Ensure the containerd user owns the devmapper snapshotter root directory.
- Mount the metadata volume read-write and monitor free space.
- Never run two containerd instances against the same pool/DB.
- Back up metadata.db before containerd upgrades.
When it happens
Trigger: Calling NewPoolMetadata (directly or through createStore/NewPoolDevice when starting the devmapper snapshotter) when the DB file cannot be opened with read-write access, is corrupted, or ensureDatabaseInitialized fails to create the required buckets.
Common situations: DB file on a read-only volume; snapshotter root path owned by a different user after a containerd upgrade; disk full; leftover corrupt db from an unclean shutdown; running two containerd instances pointed at the same metadata.db.
Related errors
- failed to query devices from metastore: %w
- failed to migrate to %s.%d: %w
- missing bucket: %w
- missing namespace %q bucket: %w
- missing mounts bucket: %w
AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02).
Data as JSON: /api/errors/5ddad78dfc31114b.
Report an issue: GitHub.