Comparison: UBI vs LittleFS vs NVS vs ZMS¶
What this page covers: A side-by-side comparison of UBI and the three main Zephyr-native storage subsystems you would otherwise reach for: LittleFS, NVS, and ZMS. The goal is to make the “which one do I pick?” decision in five minutes.
After reading this: You will know which storage abstraction matches your project’s shape — and, equally important, when UBI is the wrong answer.
Where UBI sits in the storage stack¶
UBI is not a peer of LittleFS / NVS / ZMS. It sits one layer below them: it virtualises raw flash into multiple wear-leveled, bad-block-managed logical eraseblock volumes, and a higher-level abstraction can run inside any one of those volumes.
+---------------------------------------------------------------+
| Application |
+---------------------------------------------------------------+
| Filesystem / KV store / database / custom binary blob |
+---------------------------------------------------------------+
| UBI logical eraseblock volumes |
| (named, runtime-resizable, optionally AEAD-wrapped via PSA) |
+---------------------------------------------------------------+
| Zephyr flash_area |
+---------------------------------------------------------------+
| Physical flash (raw NOR / NAND, internal or external) |
+---------------------------------------------------------------+
In the rest of this page “UBI vs LittleFS / NVS / ZMS” means “using UBI instead of that layer eating raw flash directly”.
At a glance¶
Feature |
UBI |
LittleFS |
NVS |
ZMS |
|---|---|---|---|---|
Abstraction |
Logical eraseblock volumes |
POSIX-like filesystem (files, dirs) |
Key → value store |
Key → value store |
Granularity |
Eraseblock (typ. 4–64 KB) |
File / byte |
Record (≤ entry size) |
Record (≤ entry size) |
Wear-leveling |
Global, across all PEBs |
Per-block, copy-on-write |
Cycle through reserved sectors |
Cycle through reserved sectors |
Multiple volumes / namespaces |
Yes — N named volumes per partition |
One filesystem per partition |
One namespace per partition |
One namespace per partition |
Bad-block handling |
Automatic detection + isolation |
Block-level, transparent |
None (assumes NOR) |
None (assumes NOR) |
Dynamic resize |
Yes (dynamic volumes) |
n/a (filesystem) |
n/a |
n/a |
Crash safety (metadata) |
Dual-bank reserved PEBs + monotonic |
Copy-on-write, journaled |
Sector-rotation atomic write |
Sector-rotation atomic write |
Crash safety (user data) |
No — metadata only; mid-write loses the LEB |
Yes — every write is atomic |
Yes — record write is atomic |
Yes — record write is atomic |
Authenticated encryption |
Yes, opt-in ( |
No |
No |
No |
Random-access I/O |
Yes, within a LEB |
Yes (file |
Read by key |
Read by key |
Library footprint (Cortex-M33, |
~9.5 KB plain / ~28.6 KB secure |
~25–35 KB depending on config |
~3–5 KB |
~4–6 KB |
RAM cost |
Proportional to PEB count + volume count |
File table + lookahead buffer |
Two-sector cache |
Cache + ATE table |
Tightly bound to file model |
No — block-level |
Yes |
No (KV) |
No (KV) |
Target media |
Raw NOR / NAND |
Raw NOR / NAND |
Raw NOR |
Raw NOR |
Origin |
Inspired by Linux UBI; adapted for Zephyr’s resource-constrained targets |
Upstream ARM project, integrated in Zephyr |
Zephyr-native |
Zephyr-native (successor of NVS) |
Numbers are order-of-magnitude. Exact footprint depends on Kconfig,
toolchain, and feature flags. UBI footprint figures come from the
sample build on the b_u585i_iot02a board (STM32U585) — see
Architecture Guide for the full methodology.
Picking the right one¶
Pick UBI when…¶
You have raw NOR or NAND flash (not eMMC / SD).
You need multiple independent logical volumes on the same partition — for example, an A/B firmware image pair plus a configuration blob, all sharing one wear-leveling pool.
You need global wear-leveling that NVS/ZMS cannot offer because your records are large or eraseblock-shaped.
You want a predictable, fully static RAM cost that you can size at compile time.
You may need authenticated encryption of every on-flash byte at some point in the product lifetime (turn
CONFIG_UBI_SECUREon later without rewriting your storage layer).You want to mix plain and secure storage on the same product — e.g. a plain UBI device on internal flash for hot configuration and a secure UBI device on external NOR for firmware images and secrets, both reached through the same API.
You want a substrate, not a finished abstraction. UBI gives you raw LEB read/write so a future filesystem, LSM-tree, or application-specific binary format can sit on top without reinventing wear-leveling, bad-block handling, or crash-safe metadata.
Pick LittleFS when…¶
You need a real filesystem with files, directories, and a POSIX API.
Your access pattern is fine-grained byte-level reads and writes — typical for configuration files, logs, or many small artefacts.
Power-loss atomicity for user data is a hard requirement.
You are willing to accept a larger code footprint and the inherent overhead of a journaling filesystem.
LittleFS can also be layered on top of UBI when you want both multi-volume wear-leveling and a filesystem inside one of those volumes.
Pick NVS when…¶
You only need a key → value store for a small number of configuration items.
The total data set fits comfortably inside one flash partition with a few reserved sectors.
You want the smallest possible code and RAM footprint.
Pick ZMS when…¶
Your use case looks like NVS, but you also need larger records, faster lookups at scale, or the additional features ZMS adds over NVS.
You are starting a new project today and Zephyr’s documentation steers you toward ZMS as the modern successor to NVS.
Don’t pick any of the four when…¶
Your storage is managed flash (eMMC, SD card). Use the native filesystem on top of the device’s own FTL — every layer here would just duplicate work the device already does.
Your write workload is so light that you can keep state in RAM and persist a single blob to a fixed flash partition through
flash_area_write(). None of these layers buys you anything in that regime.
Decision shortcut¶
A two-question shortcut that gets the right answer ~90 % of the time:
Do I need files, directories, or a POSIX API?
Yes → LittleFS (optionally on top of a UBI volume).
No → continue.
Do I need more than one independent storage region on the same partition, or do I anticipate needing authenticated encryption of the entire device?
Yes → UBI (plain or secure).
No → ZMS for new code, NVS if the project is already on NVS.
When the gap matters most¶
The four layers in the table above all work fine for small, single-purpose configuration storage on small internal flash. The gap that UBI exists to fill shows up when one or more of the following is true:
External NOR / NAND in the megabytes-and-up range. NVS and ZMS scale by sector count and were not designed to manage hundreds of eraseblocks. LittleFS scales but forces you into a filesystem abstraction.
Multiple independent storage regions on the same chip. A common shape: A/B firmware images, a configuration blob, a calibration table, and an event log — all on one external flash. With NVS / ZMS / LittleFS / FCB this means partitioning the chip and giving each region its own private wear-leveling pool, which wastes endurance. UBI gives all of them one global pool of physical eraseblocks and lets them grow and shrink at runtime.
Mixed write workloads. A region with frequent small writes next to a region that is written once per firmware update. Per- region wear-leveling makes the hot region wear out first; UBI spreads wear across the whole chip regardless of which volume is hot.
Bad-block handling. NVS / ZMS have no answer; LittleFS treats blocks as files. UBI detects, torture-tests, and isolates bad blocks transparently to the layer above. Mandatory on NAND, and still relevant on NOR once the device is pushed past the manufacturer’s guaranteed endurance window — worn cells stop erasing reliably and need to be retired.
Tamper-evident or rollback-detectable storage. None of LittleFS / NVS / ZMS / FCB authenticate their metadata. Secure UBI AEAD-wraps every commit-visible record (data and UBI’s own metadata) and binds an external freshness callback to the device-header revision and the VID-header global sequence number.
Encryption¶
The other three layers do not authenticate or encrypt their on-flash representation. If your product needs confidentiality or integrity for stored data, the choices are:
Encrypt at the application layer, above LittleFS / NVS / ZMS. You own key management, nonce uniqueness, AAD design, and rollback detection — all of which are easy to get wrong, and none of which protect the underlying filesystem’s own metadata.
Use Secure UBI (
CONFIG_UBI_SECURE=y). Every commit-visible on-flash structure — UBI metadata, reserved PEBs, and your LEB data — is wrapped in AES-128-CCM via PSA Crypto, with location and identity binding in AAD, versioned keys with an allowlist, a refcounted key lifecycle (KEY_ROTATE_SOON/KEY_ROTATE_NOW/KEY_RETIRABLEevents), and a sticky read-only mode on auth / RNG / budget failures. Anti-rollback is delegated to an application-supplied freshness callback bound to the device-header revision and the VID-header global sequence number.
The trade-off is footprint (~9.5 KB plain → ~28.6 KB secure on Cortex-M33) and the requirement to wire a PSA key provider, an allowlist, a freshness store, and an event callback. See Secure Architecture: Overview for the breakdown.
See also¶
What is UBI? — UBI’s own “non-goals” section re-states some of these boundaries from UBI’s perspective.
Architecture Guide — the on-flash format and resource-usage numbers behind the UBI row of the table above.
Secure Architecture: Overview — when authenticated encryption matters and what enabling
CONFIG_UBI_SECUREcosts.