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 nil

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Check permissions/ownership of the devmapper snapshotter root path and its metadata.db file; ensure the containerd user has read-write access.
  2. Confirm the filesystem holding the DB is writable and not full (df, mount | grep ro).
  3. 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).
  4. 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

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


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/5ddad78dfc31114b. Report an issue: GitHub.