Nydus RAFS Metadata Structure: Bootstrap and Blob Files Explained

Nydus stores container image filesystem metadata in a memory-mapped binary bootstrap file containing a superblock, inode table, blob table, and inode wrappers that enable fast on-demand loading via the RAFS v5 format.

The Nydus image service (dragonflyoss/nydus) uses the Registry Accelerated File System (RAFS) format to decouple filesystem metadata from actual data content. Understanding the Nydus RAFS metadata structure is essential for developers optimizing container image distribution, as the bootstrap file acts as the central index that allows nydusd to lazily fetch data chunks from remote blob files.

RAFS Bootstrap Layout Overview

The bootstrap (also called the metadata blob) is a binary file that nydusd memory-maps directly into address space. In RAFS v5—the format implemented in the current master branch—the bootstrap consists of contiguous sections laid out in a specific order.

Superblock (Header)

The first 8 KiB of the bootstrap contain the superblock, defined as RafsV5SuperBlock in rafs/src/metadata/layout/v5.rs (lines 94-124). This fixed-size header stores:

  • Magic number and version identifiers
  • Block size and feature flags
  • Total inode count
  • Byte offsets to the inode table, blob table, and prefetch table

The superblock acts as the roadmap for the runtime to locate all other metadata structures without scanning the entire file.

Inode Table

Immediately following the superblock, the Inode Table provides a flat array of 32-bit offsets (defined around line 360-378 in rafs/src/metadata/layout/v5.rs). Each entry i contains the byte offset (relative to the bootstrap start) where the i-th Inode Wrapper resides.

This design allows O(1) inode lookup: the runtime reads the inode table entry, seeks to the calculated offset, and immediately accesses the inode data.

Blob Table

The Blob Table (defined in rafs/src/metadata/layout/v5.rs, lines 524-554) describes every data blob referenced by the image. Each entry stores:

  • Blob ID (content hash)
  • Uncompressed and compressed sizes
  • Feature flags (compression algorithm, etc.)

When a file chunk needs to be fetched, the runtime consults this table to identify which blob file contains the data and where to retrieve it from remote storage or local cache.

Inode Wrapper

Each Inode Wrapper contains the actual filesystem metadata for a single file or directory. Defined in rafs/src/metadata/layout/v5.rs (around lines 886-940), the wrapper consists of:

  1. Fixed-size inode header (RafsV5Inode): Stores digest, parent inode number, UID/GID, mode, size, child index/count, name length, symlink length, timestamps, and flags.
  2. Variable-length name: The filename, aligned to 8 bytes.
  3. Symlink path: If the inode is a symlink, the target path (8-byte aligned).
  4. Extended attributes (xattrs): Optional key-value pairs.
  5. Chunk Info Table: For regular files, an array of RafsV5ChunkInfo entries (defined around line 1200) describing each data chunk's digest, compressed size, and offset within its blob.

How Bootstrap Components Work Together

The Nydus RAFS metadata structure creates a deterministic lookup chain from filename to data chunk:

  1. Path resolution starts at the root inode (index 0). The runtime reads the Inode Table entry 0 to locate the root Inode Wrapper.
  2. Directory traversal uses the child_index and child_count fields in the inode header to find consecutive Inode Table entries for directory entries.
  3. File access resolves to an Inode Wrapper containing a Chunk Info Table. Each chunk entry references a blob index and offset.
  4. Data fetching uses the blob index to look up the physical location in the Blob Table, then retrieves the specific chunk from the remote blob file.

This architecture allows nydusd to mount container images without downloading the full content, fetching only the bootstrap metadata (typically a few MB) and individual data chunks on demand.

Building and Loading Bootstrap Files

Building with nydus-image

The nydus-image tool constructs the bootstrap by walking the source filesystem tree and serializing the metadata structures. In builder/src/core/bootstrap.rs (lines 20-85), the Bootstrap struct orchestrates this process:

use builder::Bootstrap;
use builder::{BootstrapContext, BuildContext, Tree};

// `tree` is a populated `Tree` representing the source filesystem.
let mut bootstrap = Bootstrap::new(tree)?;
bootstrap.build(&mut build_ctx, &mut bootstrap_ctx)?;
bootstrap.dump(
    &mut build_ctx,
    &mut Some(ArtifactStorage::FileDir(PathBuf::from("out"), "bootstrap".into())),
    &mut bootstrap_ctx,
    &blob_table,
)?;

The build method assigns inode numbers and child indexes, while dump serializes the RafsV5SuperBlock, inode table, and inode wrappers into the final binary layout.

Runtime Loading with nydusd

At runtime, nydusd loads the bootstrap via RafsSuper::load_from_file defined in rafs/src/metadata/mod.rs (lines 51-57):

use rafs::RafsSuper;
use std::sync::Arc;
use std::path::Path;

let (mut rafs, mut reader) = RafsSuper::load_from_file(
    Path::new("/path/to/bootstrap"),
    Arc::new(config.clone()),
    false,                // not a chunk-dict bootstrap
)?;
rafs.validate()?;        // integrity checks (flags, digests, etc.)

This function reads the superblock, validates the magic numbers and version, then memory-maps the remaining sections for direct access.

Traversing Inodes

Once loaded, the filesystem implementation in rafs/src/fs.rs traverses inodes using the offset calculations:

let root = rafs.root_inode()?; // first inode (index = RAFS_V5_ROOT_INODE)
let mut stack = vec![root];

while let Some(inode) = stack.pop() {
    println!("inode {}: mode=0x{:x}", inode.ino(), inode.mode());

    if inode.is_dir() {
        // child indexes are stored in the inode table
        let first = inode.child_index();
        let count = inode.child_count();
        for i in 0..count {
            let offset = rafs.superblock.get_inode_offset(first + i)?;
            let child = rafs.get_inode_by_offset(offset)?;
            stack.push(child);
        }
    }
}

The get_inode_offset method performs the O(1) lookup into the inode table, while child_index and child_count come from the RafsV5Inode header fields.

Key Source Files and Implementation Details

File Role Direct Link
rafs/src/metadata/layout/v5.rs Defines on-disk structures: RafsV5SuperBlock, RafsV5Inode, RafsV5BlobTable, RafsV5InodeTable, and RafsV5ChunkInfo. https://github.com/dragonflyoss/nydus/blob/master/rafs/src/metadata/layout/v5.rs
builder/src/core/bootstrap.rs Implements the builder that walks the source tree, assigns inode numbers, and writes the bootstrap blob. https://github.com/dragonflyoss/nydus/blob/master/builder/src/core/bootstrap.rs
rafs/src/metadata/mod.rs High-level RafsSuper wrapper providing load_from_file and validation logic. https://github.com/dragonflyoss/nydus/blob/master/rafs/src/metadata/mod.rs
rafs/src/fs.rs Runtime filesystem implementation that uses the loaded bootstrap to resolve inodes and chunks. https://github.com/dragonflyoss/nydus/blob/master/rafs/src/fs.rs
docs/nydus-design.md Human-readable architecture overview of the bootstrap format. https://github.com/dragonflyoss/nydus/blob/master/docs/nydus-design.md
docs/nydus-image.md CLI documentation for bootstrap creation. https://github.com/dragonflyoss/nydus/blob/master/docs/nydus-image.md

Summary

  • Nydus RAFS metadata structure centers on a single binary bootstrap file that is memory-mapped at runtime, eliminating the need for parsing during mount.
  • The superblock (first 8 KiB) acts as the header, storing magic numbers, version info, and offsets to all other tables.
  • The inode table provides O(1) lookup to inode wrappers, which contain file metadata, names, xattrs, and chunk information.
  • The blob table maps logical blob indexes to physical data blobs, enabling the runtime to fetch specific chunks on demand from remote storage.
  • This architecture allows nydusd to mount container images instantly while fetching only the data chunks actually accessed by the workload.

Frequently Asked Questions

What is the difference between a bootstrap file and a blob file in Nydus?

The bootstrap file contains the filesystem metadata—superblock, inode table, directory entries, and chunk indices—while blob files contain the actual file data content. The bootstrap is typically a few megabytes and is downloaded completely at mount time, whereas blobs can be gigabytes in size and are fetched lazily chunk-by-chunk as the application reads files.

How does Nydus RAFS v5 store file metadata?

RAFS v5 stores file metadata in Inode Wrappers within the bootstrap file. Each wrapper contains a fixed-size RafsV5Inode structure with fields for mode, ownership, size, and timestamps, followed by variable-length filename, symlink target, extended attributes, and a chunk info table for regular files. The Inode Table provides byte offsets to locate these wrappers in O(1) time.

What is the role of the superblock in Nydus bootstrap?

The superblock occupies the first 8 KiB of the bootstrap and serves as the file header. Defined as RafsV5SuperBlock in rafs/src/metadata/layout/v5.rs, it contains magic bytes to identify the format, version numbers, block size, total inode count, and critical byte offsets pointing to the inode table, blob table, and prefetch table. The runtime validates this header before mapping the rest of the file.

How does the blob table enable on-demand loading?

The Blob Table maps integer blob indices (stored in each chunk info entry) to physical storage locations. When a file access triggers a page fault, the runtime looks up the file's chunks in the Chunk Info Table, extracts the blob index for the required chunk, and queries the Blob Table to determine which remote blob file to fetch. This indirection allows a single bootstrap to reference multiple data blobs stored on different backends (registry, OSS, local disk) while maintaining a compact metadata representation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →