# Nydus RAFS Metadata Structure: Bootstrap and Blob Files Explained

> Understand the Nydus RAFS metadata structure. Learn how bootstrap and blob files enable fast on-demand loading of container images.

- Repository: [dragonflyoss/nydus](https://github.com/dragonflyoss/nydus)
- Tags: internals
- Published: 2026-02-28

---

**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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/builder/src/core/bootstrap.rs) (lines 20-85), the `Bootstrap` struct orchestrates this process:

```rust
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`](https://github.com/dragonflyoss/nydus/blob/main/rafs/src/metadata/mod.rs) (lines 51-57):

```rust
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`](https://github.com/dragonflyoss/nydus/blob/main/rafs/src/fs.rs) traverses inodes using the offset calculations:

```rust
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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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`](https://github.com/dragonflyoss/nydus/blob/main/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.