File Systems for Embedded Devices
File systems provide the organizational framework for storing, retrieving, and managing data on storage media in embedded devices. Unlike desktop and server systems where file systems primarily optimize for large capacity drives and complex user interactions, embedded file systems must address unique challenges including limited resources, flash memory characteristics, power-fail safety, and real-time performance requirements.
Selecting the appropriate file system for an embedded application requires understanding the underlying storage technology, access patterns, reliability requirements, and resource constraints. This article explores the major file system options available for embedded development, from traditional disk-oriented systems adapted for embedded use to specialized file systems designed specifically for flash memory devices.
Storage Media Characteristics
The choice of file system depends heavily on the characteristics of the underlying storage media. Understanding these characteristics is essential for selecting a file system that maximizes performance and device longevity.
Flash Memory Fundamentals
Flash memory has become the dominant storage technology in embedded systems due to its solid-state reliability, low power consumption, and decreasing cost. However, flash memory presents unique characteristics that significantly impact file system design.
Erase-before-write requirement: Flash memory cannot overwrite existing data directly. Before writing new data to a location, the existing data must first be erased. This fundamental characteristic requires file systems to track which areas contain valid data and manage erasure operations carefully.
Block-based erasure: While flash memory can be read and written at relatively fine granularity, erasure operates on much larger blocks. NAND flash programs whole pages, historically 512 bytes but 2 KB to 16 KB on current devices (plus a spare area for metadata and error-correction codes), and erases blocks that range from 128 KB on legacy small-page parts to several megabytes on modern three-dimensional NAND. NOR flash writes at word granularity but erases sectors that typically span 4 KB to 256 KB. This asymmetry between write granularity and erase granularity creates the need for sophisticated management strategies.
Limited write endurance: Each flash memory cell can only endure a finite number of program-erase cycles before wearing out. Endurance falls sharply as more bits are stored per cell: single-level cell (SLC) NOR and NAND are commonly rated near 100,000 cycles, multi-level cell (MLC) parts in the range of a few thousand to ten thousand, triple-level cell (TLC) parts around 1,000 to 3,000 cycles, and quad-level cell (QLC) parts only a few hundred. Industrial parts often run TLC silicon in a pseudo-SLC mode to trade capacity for an order of magnitude more endurance. File systems must distribute writes evenly across the device to maximize overall device lifetime.
Write amplification: A single logical byte written by an application can trigger far more flash programming than its size suggests, because metadata updates, partial-page writes, and garbage collection all consume additional program-erase cycles. Write amplification is the ratio of physical bytes written to logical bytes requested, and reducing it is the single most effective way to extend the life of a flash-based device.
Read disturb: Repeatedly reading from a NAND block without intervening erasure applies a small stress to the cells of the pages that are not being read, and their stored charge can drift until bits flip. Controllers and flash-aware file systems track read counts and rewrite a block elsewhere before its error rate approaches the correction limit of the error-correcting code.
Bit errors and ECC: NAND flash is not error-free by design. Every read passes through an error-correcting code, historically Hamming or BCH and now predominantly low-density parity-check (LDPC) codes, whose strength must match the requirements published in the manufacturer's datasheet. Raw-NAND file systems must therefore sit above a driver or controller that supplies the correct ECC strength.
NOR versus NAND Flash
The two primary types of flash memory present different trade-offs for embedded applications:
NOR flash provides true random access, allowing a processor to execute code directly from the flash (execute-in-place, or XIP) over a parallel bus or a memory-mapped quad or octal SPI interface. NOR flash offers low read latency and ships free of factory defects, but it costs far more per bit, tops out at densities measured in hundreds of megabits to a few gigabits, and erases slowly. It is the usual home for boot loaders, firmware images, and small configuration stores in embedded systems.
NAND flash provides much higher density and lower cost per bit, making it suitable for mass storage. NAND is accessed page by page through an indirect command interface rather than a memory map, so code cannot execute from it directly. NAND devices also ship with factory-marked bad blocks and develop further bad blocks in service, so the file system or an intervening layer must maintain a bad-block table and skip the affected blocks. NAND dominates in applications requiring significant capacity, such as data logging, multimedia storage, and removable media.
Managed Flash Devices
Many embedded systems use managed flash devices such as SD and microSD cards, eMMC (embedded MultiMediaCard), UFS (Universal Flash Storage) in higher-performance mobile designs, small NVMe solid-state drives, and USB flash drives. These devices include integrated controllers that handle wear leveling, bad block management, and error correction, presenting a block device interface similar to traditional hard drives.
While managed flash devices simplify file system selection by hiding flash-specific characteristics, they introduce their own considerations. The internal management algorithms may conflict with file system optimizations, and the quality of wear leveling varies significantly between manufacturers and price points. Critical applications may still benefit from file systems that minimize write amplification even when using managed flash.
FAT File Systems
The File Allocation Table (FAT) family of file systems remains ubiquitous in embedded systems due to its simplicity, wide compatibility, and minimal resource requirements. Originally developed for floppy disks in the late 1970s, FAT has evolved through several versions while maintaining backward compatibility.
FAT Architecture
FAT file systems organize storage into clusters, with a central file allocation table tracking the status and linkage of each cluster. The file allocation table serves as both a bitmap indicating free and used clusters and a linked list connecting the clusters belonging to each file.
The directory structure uses fixed-size entries containing file names, attributes, timestamps, starting cluster numbers, and file sizes. The original FAT specification limited file names to eight characters with a three-character extension (8.3 format), though later extensions support long file names while maintaining backward compatibility.
FAT Variants
FAT12 uses 12-bit cluster addresses and is limited to 4,084 data clusters, which confines it to volumes of a few tens of megabytes at most. It survives on floppy-format media, on very small serial flash partitions, and inside legacy systems, but it is obsolete for primary storage.
FAT16 uses 16-bit cluster addresses with a maximum of 65,524 data clusters. Combined with the 32 KB cluster ceiling that most implementations honor, that caps volumes at 2 GB; implementations that accept 64 KB clusters reach 4 GB at the cost of severe internal fragmentation. FAT16 remains common in smaller embedded applications where simplicity and compatibility outweigh the capacity limit.
FAT32 uses 32-bit cluster entries of which only 28 bits carry the cluster number. Because the volume is addressed with a 32-bit sector count, a device with conventional 512-byte sectors is limited to 2 TB. Individual files remain capped at one byte less than 4 GB, an inherited limit that rules FAT32 out for large media files. Windows will format FAT32 volumes only up to 32 GB, but that is a tool restriction rather than a property of the format, and other operating systems create larger FAT32 volumes freely.
exFAT (Extended FAT) was introduced in 2006 for flash media. It removes the 4 GB file-size ceiling and the FAT32 volume limits, replaces the allocation-table walk with a free-space bitmap for contiguous files, and lets the cluster size be aligned to the erase block of the underlying device. Microsoft published the exFAT specification in 2019 and supported the file system's inclusion in the Open Invention Network's Linux System Definition; a native exFAT driver has shipped in the mainline Linux kernel since version 5.4, so the licensing friction that once discouraged its use in embedded products has largely dissolved.
FAT Advantages and Limitations
FAT file systems offer several advantages for embedded applications:
Universal compatibility: Virtually every operating system can read and write FAT file systems, making FAT ideal for removable media and data interchange.
Simplicity: The FAT structure is straightforward to implement, with numerous open-source implementations available. Memory requirements are modest, with the file allocation table being the primary RAM consumer.
Deterministic performance: FAT operations are relatively predictable, though worst-case performance can be poor for fragmented files or nearly full volumes.
However, FAT has significant limitations:
No power-fail safety: FAT lacks journaling or other mechanisms to ensure file system consistency after unexpected power loss. Corrupted file allocation tables can render data inaccessible.
No wear leveling: FAT was designed for magnetic media and does not distribute writes to maximize flash lifetime. The file allocation table itself becomes a wear hotspot as it is updated with every file operation.
Limited metadata: FAT lacks support for access permissions, symbolic links, and other features expected in Unix-like environments.
Linux Native File Systems
Embedded Linux systems often use file systems from the ext (extended file system) family, which provide more sophisticated features than FAT while remaining well-supported and mature.
ext2 File System
The second extended file system (ext2) was the standard Linux file system for many years before journaling file systems became prevalent. ext2 divides storage into block groups, each containing a superblock copy, block and inode bitmaps, an inode table, and data blocks.
ext2 supports Unix permissions, symbolic links, and file attributes that FAT lacks. For embedded systems where journaling overhead is undesirable and power-fail safety is managed through other means (such as read-only root file systems), ext2 provides a capable and efficient option.
The lack of journaling makes ext2 faster for write operations and reduces flash wear compared to journaling file systems. However, unexpected power loss can leave the file system in an inconsistent state requiring a full file system check during the next boot.
The dedicated ext2 driver in the Linux kernel was marked deprecated in version 6.9 because it stores inode timestamps as 32-bit values and therefore cannot represent dates after 19 January 2038. The on-disk format itself is not going away: kernels built with CONFIG_EXT4_USE_FOR_EXT2 mount ext2 volumes with the ext4 driver, which handles the older layout correctly and resolves the timestamp problem when the volume uses 256-byte inodes. New embedded designs should assume the ext4 driver and treat "ext2" as a set of format options rather than a separate implementation.
ext3 and ext4 File Systems
ext3 added journaling capability to ext2, recording pending changes to a journal before committing them to the file system proper. This journaling ensures that the file system can be quickly recovered to a consistent state after unexpected power loss, eliminating the need for lengthy file system checks.
ext3 offers three journaling modes: journal (safest but slowest, journaling both metadata and data), ordered (default, journaling metadata while ensuring data is written before metadata), and writeback (fastest but provides only metadata consistency guarantees).
ext4 extended ext3 with numerous improvements including extents for more efficient large file handling, delayed allocation for improved performance, metadata checksums, and support for larger volumes and files. Later additions such as fast commits, introduced in Linux 5.10, cut the amount of journal traffic generated by fsync-heavy workloads, which directly reduces flash wear. ext4 has become the default file system for most Linux distributions and for a large share of embedded Linux products on eMMC.
For embedded Linux systems with flash storage, ext4 with appropriate mount options can provide good performance. Disabling the journal (mounting as ext2) or using journaling modes appropriate for flash can reduce write amplification. However, ext3/ext4 were not designed specifically for flash memory and may not provide optimal wear distribution.
Considerations for Embedded Use
When using ext file systems on embedded flash storage, several considerations apply:
Mount options: Options like noatime (disabling access time updates), commit interval adjustments, and barrier settings can significantly impact write patterns and flash wear.
Journal placement: If using journaling, placing the journal on a more durable storage area or using external journaling can extend flash life.
Read-only root: Many embedded systems use a read-only root file system with a separate writable partition for variable data, eliminating wear concerns for the bulk of the file system.
Flash-Specific File Systems
Several file systems have been designed specifically to address the unique characteristics of flash memory, providing integrated wear leveling, power-fail safety, and efficient flash utilization.
JFFS2
The Journaling Flash File System 2 (JFFS2) is a log-structured file system designed specifically for raw flash memory in embedded Linux systems. JFFS2 writes data sequentially to flash, treating the entire flash device as a circular log.
Log-structured design: Rather than updating files in place, JFFS2 appends a new node describing the change to the next free space in an already-erased block and marks the superseded node obsolete. Erasure is not avoided, but it is deferred out of the write path and handed to garbage collection, and because the append point advances continuously the writes spread naturally across the device.
Garbage collection: As the flash fills with a mix of valid and obsolete data, JFFS2 performs garbage collection to reclaim space. The garbage collector selects blocks containing mostly obsolete data, copies any valid data to new locations, and erases the blocks for reuse.
Power-fail safety: The log-structured design provides inherent power-fail safety. Because new data is always written to new locations, a power failure never corrupts existing valid data. At mount time, JFFS2 scans the flash to reconstruct the file system state.
Compression: JFFS2 supports transparent data compression, which can significantly increase effective storage capacity for compressible data while reducing the amount of flash written.
JFFS2 limitations follow directly from that design. Mounting requires scanning every node on the device, so mount time and RAM consumption both grow with the size and occupancy of the flash rather than staying constant. The in-memory index must be held for the whole file system, which becomes prohibitive beyond a few tens of megabytes. For these reasons JFFS2 is now generally restricted to small NOR partitions, with UBIFS preferred for anything larger.
YAFFS and YAFFS2
Yet Another Flash File System (YAFFS) was designed specifically for NAND flash memory. YAFFS2, the current version, improves on the original to support larger page sizes and newer NAND technologies.
NAND optimization: YAFFS2 is structured around NAND flash characteristics, with objects (files and directories) stored as collections of chunks that map efficiently to NAND pages. The design minimizes the number of page writes required for file system operations.
Checkpoint mechanism: Unlike JFFS2, YAFFS2 can save checkpoints of the file system state, dramatically reducing mount time for large file systems. The checkpoint captures the in-memory data structures, allowing rapid reconstruction without scanning all flash blocks.
Memory efficiency: YAFFS2 uses fixed-size data structures that scale linearly with the number of objects in the file system, providing more predictable memory consumption than JFFS2.
YAFFS2 is dual-licensed, offered under the GPL for Linux use and under a separate commercial license from Aleph One for products that cannot accept GPL terms, which made it attractive to proprietary RTOS ports. It saw heavy service in early Android handsets and in embedded Linux devices with raw NAND. It was never merged into the mainline Linux kernel, however, so it must be carried as an out-of-tree patch, and most new raw-NAND designs now choose UBIFS instead.
UBIFS
UBIFS, the UBI file system, works on top of UBI (Unsorted Block Images), a volume management layer for raw flash. This layered architecture separates wear leveling and bad block management, handled by UBI, from file system functionality, handled by UBIFS. Both were developed at Nokia with the University of Szeged and have been part of the mainline Linux kernel since version 2.6.27.
UBI layer: UBI presents logical erase blocks that hide the complexity of physical flash management. It performs static wear leveling across the entire device, retires bad blocks transparently, tracks erase counts in a header written to every block, and provides atomic logical erase block updates. UBI also lets several volumes be resized dynamically within one flash partition. The cost is overhead: UBI reserves a percentage of blocks for bad-block handling, spends two pages per block on headers, and must scan the device at attach time, although the fastmap feature added in Linux 3.7 largely removes that scan.
Scalability: UBIFS was designed to scale well to large flash devices where JFFS2 performance degrades. Mount times remain fast regardless of flash size because UBIFS uses an on-flash index rather than scanning the entire device.
Write-back caching: UBIFS supports write-back caching for improved performance, with configurable synchronization policies to balance performance against power-fail safety.
Compression: Like JFFS2, UBIFS supports transparent compression with multiple algorithm options.
UBIFS has become the recommended file system for raw flash in modern embedded Linux systems, offering the best combination of performance, reliability, and scalability for large NAND flash devices.
F2FS
The Flash-Friendly File System (F2FS) was developed by Samsung for use with modern flash storage devices including solid-state drives, eMMC, UFS, and SD cards, and was merged into the mainline Linux kernel in version 3.8. Unlike JFFS2 and UBIFS, which target raw flash, F2FS works with managed flash devices that present a block device interface.
Log-structured design: F2FS uses a log-structured approach optimized for flash, with modifications to address the classic weaknesses of log-structured file systems. A node address table breaks the "wandering tree" problem, in which updating a data block would otherwise cascade into rewrites of every index block above it. The multi-head logging design maintains six active logs that separate data from metadata and hot from cold content, so blocks with similar update frequency land together and garbage collection has to move far less valid data.
Flash awareness: F2FS aligns file system structures with flash erase block boundaries and optimizes write patterns to work harmoniously with flash translation layer algorithms in managed devices.
Performance features: F2FS includes numerous performance optimizations including adaptive logging, hot/cold data separation, and multi-stream support for devices that expose this capability.
F2FS is particularly well-suited for Android devices and embedded Linux systems using eMMC or high-quality SD cards where its flash-optimized design can significantly improve both performance and device longevity.
Flash Translation Layers
Flash translation layers (FTL) provide a block device abstraction on top of raw flash memory, allowing traditional block-oriented file systems to be used with flash storage. Understanding FTL concepts is essential for making informed file system decisions.
FTL Fundamentals
An FTL maps logical block addresses (as seen by the file system) to physical flash locations. When the file system writes to a logical block, the FTL writes the data to a new physical location and updates its mapping tables. This indirection allows the FTL to perform wear leveling and handle the erase-before-write requirement transparently.
Mapping granularity: FTLs may use page-level mapping (finest granularity, best performance, highest memory consumption), block-level mapping (coarsest granularity, lowest overhead, worst performance for small random writes), or hybrid approaches that balance these trade-offs.
Garbage collection: Like flash file systems, FTLs must reclaim space occupied by obsolete data. FTL garbage collection operates transparently to the file system but can cause significant performance variations, particularly when the device is nearly full.
Hardware FTL: Managed Flash Devices
SD cards, eMMC, USB flash drives, and SSDs all contain integrated controllers running FTL firmware. The quality and sophistication of these FTLs varies enormously:
Consumer-grade devices: Inexpensive SD cards and USB drives may use simple FTLs optimized for sequential write patterns typical of consumer applications. Performance can degrade dramatically under random write workloads common in embedded systems.
Industrial-grade devices: Industrial SD cards and eMMC modules typically include more sophisticated FTLs with better wear leveling, power-fail protection, and consistent performance characteristics. These devices cost more but provide reliability appropriate for embedded applications.
Discard support: Modern managed flash devices accept a command that tells the controller a range of logical blocks no longer holds useful data: TRIM on SATA, UNMAP on SCSI and UFS, ERASE or DISCARD on eMMC, and Dataset Management Deallocate on NVMe. Linux exposes all of these through the generic discard path, reachable with the discard mount option or a periodic fstrim run. Supplying this information lets the FTL skip copying dead data during garbage collection, which both preserves write performance and reduces write amplification. Periodic fstrim is usually preferable to the discard mount option, because inline discards add latency to every delete.
Power-fail behavior: An FTL keeps mapping tables in volatile memory and flushes them opportunistically. A device that loses power mid-update can therefore corrupt data the file system believed was already committed, or in the worst case lose an entire mapping region and brick the medium. Industrial cards and eMMC parts advertise power-loss protection, often implemented with on-board capacitance plus a journaled mapping table; consumer parts generally do not. No file system can fully compensate for an FTL that fails this way, which is why power-fail testing must be performed on the exact storage part that will ship.
Software FTL Solutions
For systems using raw flash where a block device interface is desired, software FTL solutions provide the translation layer:
mtdblock: The simplest Linux option, mtdblock provides a basic block device interface over MTD (Memory Technology Device) flash. It performs no wear leveling and caches a whole erase block in RAM to service a small write, so it is appropriate only for read-only images such as SquashFS and must never carry a read-write file system.
UBI block: The ubiblock driver exposes a read-only block device on top of a UBI volume, so a compressed read-only root file system can sit above UBI and still benefit from its wear leveling and bad-block handling. This is the standard way to run SquashFS on raw NAND.
Application-specific FTLs: Some applications implement custom FTL functionality tailored to their specific access patterns and reliability requirements.
Wear Leveling Strategies
Wear leveling distributes write and erase operations across flash memory to maximize device lifetime. Without wear leveling, frequently written locations would wear out while other areas remain underutilized.
Dynamic Wear Leveling
Dynamic wear leveling distributes writes among blocks that are currently available for writing. When a block is written, it is selected from a pool of free erased blocks based on their wear counts, preferring less-worn blocks.
Dynamic wear leveling is simple to implement and effective when all data is frequently updated. However, it cannot address wear imbalance caused by static data that is written once and never modified. Blocks containing static data never participate in the wear distribution.
Static Wear Leveling
Static wear leveling extends dynamic wear leveling by periodically relocating static data from less-worn blocks to more-worn blocks. This ensures that even blocks containing rarely-changed data participate in overall wear distribution.
Static wear leveling adds complexity and overhead, as it requires tracking wear counts for all blocks and periodically copying data that does not need to move for other reasons. The trade-off is improved wear distribution and longer device lifetime, particularly important for devices with mixed static and dynamic data.
File System Integration
Flash-native file systems like JFFS2, YAFFS2, and UBIFS integrate wear leveling directly into their operation. Their log-structured designs naturally distribute writes, and they explicitly track and optimize wear distribution.
When using block-oriented file systems on flash, wear leveling depends on the underlying FTL (either hardware in managed devices or software like UBI). The file system can help by avoiding unnecessary writes (using appropriate mount options) and providing discard hints when blocks become free.
Power-Fail Safety
Embedded systems often cannot guarantee clean shutdowns. Power may be lost unexpectedly due to battery depletion, power supply interruption, or system resets. File systems must handle these events without data corruption or file system damage.
Journaling
Journaling file systems record pending changes to a journal before committing them to the main file system structures. If power fails during an operation, the file system can replay or discard the journal entries to reach a consistent state.
Journaling protects file system structure, not necessarily file contents. The ext3 and ext4 default, data=ordered, guarantees that metadata never points at stale blocks but does not guarantee that a partially written file is complete. Journaling also adds write overhead that is worst for metadata-heavy workloads, and the journal occupies a fixed set of logical blocks that are rewritten constantly. On raw flash that would be a fatal wear hotspot; on managed flash or above UBI the translation layer relocates the underlying physical blocks, so the hotspot is logical rather than physical. It remains a source of write amplification either way.
Log-Structured Approaches
Log-structured file systems such as JFFS2, UBIFS, and F2FS obtain power-fail safety from their append-only structure rather than from a separate journal. New data always lands in previously erased space, so a power failure cannot damage the data it supersedes; the worst case is that the newest, incomplete record is discarded. JFFS2 recovers by scanning for the last complete node, while UBIFS and F2FS write periodic checkpoints and replay only the log written since the most recent one, which keeps recovery time bounded regardless of volume size.
Copy-on-Write
Copy-on-write file systems never overwrite existing data. Instead, modifications create new copies of affected blocks, with parent blocks updated to point to the new copies. This approach, used by file systems like Btrfs and ZFS, provides atomic updates and snapshot capabilities.
The same principle appears in miniature in LittleFS, whose alternating metadata block pairs are a copy-on-write commit scheme sized for a microcontroller. Copy-on-write provides excellent data safety and, on Btrfs and ZFS, end-to-end checksums that detect silent corruption. The costs are higher write amplification, since a small change propagates rewrites up the block tree, and a substantial RAM and code footprint that puts Btrfs and ZFS out of reach of most embedded targets.
Application-Level Considerations
Even a power-safe file system only guarantees that the file system itself survives; it says nothing about whether the application's data is meaningful. A file system may legitimately preserve a configuration file that was truncated but not yet rewritten.
The standard remedy is the write-to-temporary-then-rename pattern: write the new content to a temporary file, call fsync on it, rename it over the original, then call fsync on the containing directory. Because rename is atomic, a reader afterwards sees either the complete old file or the complete new one. Both synchronization calls are required, and the directory fsync is the step most often omitted.
Where several files must change together, an A/B scheme is more robust than any single-file trick: maintain two complete copies of the data set plus a small sequence number and checksum, write the inactive copy, verify it, and only then advance the sequence number. The same structure underpins dual-bank firmware update schemes, and it degrades gracefully even when the underlying storage controller misbehaves during a power cut.
Lightweight and Specialized File Systems
Resource-constrained embedded systems may require file systems with minimal memory and code footprint. Several options target these requirements.
LittleFS
LittleFS is a small fail-safe file system designed for microcontrollers. It originated inside Arm's Mbed OS project, is distributed under the permissive three-clause BSD license, and is now maintained as an independent portable C library that any RTOS or bare-metal application can adopt. It targets raw SPI NOR and NAND flash that lack an internal controller, and it reaches the flash through a four-function driver interface (read, program, erase, sync) that the integrator supplies.
Bounded RAM usage: LittleFS allocates a fixed set of buffers sized from the block geometry rather than from the volume size or file count, so a device with a few kilobytes of RAM can mount a multi-megabyte volume. Nothing scales with the number of files.
Power-fail resilience: Metadata lives in pairs of blocks that are updated alternately, and file data is written copy-on-write, so every committed state is atomic. An interruption at any point leaves either the old state or the new one, never a partial mixture, and no repair pass is required at mount.
Wear leveling: LittleFS performs dynamic wear leveling. Metadata pairs rotate between their two blocks, and a configurable block-cycle counter forces the allocator to move even long-lived metadata, which recovers part of the benefit of static wear leveling without tracking per-block erase counts.
The trade-off for this small footprint is throughput: the design favors resilience and bounded memory over speed, and directory traversal costs grow with the number of entries. LittleFS is nonetheless the default choice for connected sensors, wearables, and other microcontroller systems where a full flash file system would not fit.
SPIFFS
The SPI Flash File System (SPIFFS) targets SPI NOR flash commonly used in microcontroller applications. SPIFFS provides wear leveling and operates efficiently on flash devices ranging from 128 KB to several megabytes.
SPIFFS uses a flat namespace with no real subdirectories, which minimizes complexity and RAM usage. That limitation suits applications needing simple configuration or key-value storage rather than hierarchical organization. SPIFFS offers no power-fail safety guarantee, its mount time and performance degrade as the volume fills, and upstream development has effectively stopped. Espressif's ESP-IDF now recommends LittleFS for new designs, and SPIFFS should be regarded as a legacy option maintained for existing products.
FatFs
FatFs is a widely deployed FAT implementation for embedded systems, written in portable C with no dependencies beyond a handful of disk-I/O callbacks the integrator provides. It supports FAT12, FAT16, FAT32, and exFAT, and a long list of compile-time options lets the developer trade features such as long file names, code pages, and re-entrancy against code size. A cut-down variant, Petit FatFs, runs in well under a kilobyte of RAM for read-mostly applications. Vendor SDKs and RTOS middleware frequently ship FatFs, sometimes rebranded, as their default storage stack.
FatFs inherits every FAT weakness: no wear leveling, no power-fail safety, and no permissions. Its value lies in footprint and in the fact that a card written by the device can be read on any desktop, so it is the right answer for removable media and the wrong answer for a log partition on soldered flash.
Read-Only File Systems
For systems where file system contents do not change during operation, read-only file systems provide simplicity and reliability:
SquashFS: The default choice for embedded Linux root file systems. SquashFS compresses data in fixed-size blocks and supports gzip, LZO, LZ4, XZ, and Zstandard, letting the designer trade compression ratio against decompression cost. On raw NAND it is normally layered over a UBI volume through ubiblock; on managed flash it sits directly on a partition. Because a compressed image reads fewer bytes from the medium, it often outperforms an uncompressed file system despite the decompression work.
EROFS: The Enhanced Read-Only File System, contributed by Huawei and merged in Linux 5.4, was designed to beat SquashFS on read latency at comparable compression ratios by using fixed-output-size compression and a compact metadata layout. Android has adopted it for read-only partitions, and it is available in Yocto and Buildroot for embedded Linux images.
CramFS: An older compressed read-only file system with lower memory requirements than SquashFS but weaker compression and hard ceilings on both individual file size and total image size. It is now rarely chosen for new designs, although it retains a niche for tiny initial RAM disks.
ROMFS: A minimal uncompressed read-only file system with almost no code or metadata overhead, used chiefly on very small systems and on no-MMU targets where execute-in-place from a linearly mapped image matters more than image size.
Read-only file systems eliminate wear concerns for stored data and provide inherent protection against corruption. Many embedded systems use a read-only root file system combined with a small writable partition for configuration and runtime data.
File System Selection Guidelines
Choosing the right file system requires evaluating multiple factors against application requirements.
Storage Media Type
Raw NAND flash: UBIFS over UBI is the default answer for modern raw NAND under Linux, with a read-only SquashFS or EROFS image on ubiblock for the root file system and a UBIFS volume for writable data. YAFFS2 remains serviceable on legacy designs and on non-Linux systems that already carry a port, but its out-of-tree status makes it a poor starting point today. JFFS2 is now reasonable only on small NOR partitions.
Raw NOR flash: JFFS2 or UBIFS both work; UBIFS is preferred once the partition exceeds a few megabytes. On microcontrollers without Linux, LittleFS is the usual choice for SPI NOR. Execute-in-place code is normally kept in a raw partition outside any file system rather than inside one.
Managed flash (eMMC, SD, UFS): ext4 with tuned mount options is the safe, well-supported default. F2FS offers measurably better performance and lower write amplification on quality managed devices and is the standard choice on Android. FAT32 or exFAT is necessary only where a desktop computer must read the medium directly.
Microcontroller-class serial flash: LittleFS where power-fail safety matters, FatFs where interchange with a host computer matters, and a purpose-built wear-leveled key-value store where the payload is really a few hundred bytes of configuration rather than files.
Reliability Requirements
Power-fail safety critical: UBIFS, JFFS2, YAFFS2, LittleFS, and F2FS keep the file system consistent across an abrupt power loss without a repair pass. Journaled ext3 and ext4 protect metadata but not the contents of files in flight. FAT protects nothing, so any FAT-based design that cannot guarantee a clean shutdown needs hold-up energy from a supercapacitor or battery, a supply-monitor interrupt that triggers an orderly unmount, or both.
Data integrity critical: UBIFS checksums every node it writes, F2FS and ext4 checksum metadata, and both Btrfs and ZFS checksum data as well. Where the medium itself is suspect, add application-level verification: store a checksum with each record and validate it on read rather than trusting the storage stack.
Performance Requirements
Fast mount required: UBIFS and F2FS mount quickly regardless of size. YAFFS2 with checkpoints also mounts quickly. JFFS2 mount time increases with flash size.
Consistent latency required: Consider the garbage collection behavior of chosen file system. UBIFS and F2FS offer more predictable performance than JFFS2.
High throughput required: F2FS and ext4 generally offer better throughput than JFFS2 on managed flash. For raw flash, UBIFS provides good throughput with proper UBI configuration.
Resource Constraints
Limited RAM: LittleFS and SPIFFS are designed for memory-constrained microcontrollers. FatFs can operate with very small buffers. JFFS2 memory consumption scales with file system content and may be prohibitive for large devices with limited RAM.
Limited code space: LittleFS, SPIFFS, and FatFs have small code footprints. Full-featured file systems like UBIFS and F2FS require substantially more code space.
Implementation Best Practices
Successful file system deployment in embedded systems requires attention to configuration, testing, and operational practices.
Mount Options and Configuration
Appropriate mount options significantly impact file system behavior:
Disable unnecessary updates: Mounting with noatime suppresses the access-time write that would otherwise turn every read into a write. Linux defaults to relatime, which is better than the original behavior but still writes; on a read-mostly embedded workload noatime is nearly always correct.
Synchronization policy: Configure write-back behavior to match the power-fail requirement. On ext4 the commit interval sets how long dirty data may sit in cache, and data=journal buys data consistency at roughly double the write volume. UBIFS mounted with sync commits every operation immediately, which is safe but slow; the default deferred behavior is faster and still leaves the file system consistent, though recent writes can be lost.
Compression: UBIFS supports LZO, zlib, and Zstandard per volume. Compression reduces both storage consumption and the number of bytes programmed into flash, so it usually extends device life as well as capacity, but it costs CPU time on every access and yields nothing on data that is already compressed, such as images or encrypted payloads.
Reserve and threshold settings: ext4 reserves five percent of a volume for the superuser by default, which is wasteful on a dedicated data partition and can be lowered with tune2fs -m. F2FS and flash file systems generally need free space kept in hand for garbage collection; planning a partition on the assumption that it will run at full capacity is a common and expensive mistake.
Partition Layout
Thoughtful partition layout improves system reliability and maintainability:
Separate static and dynamic data: Place infrequently-changing data (firmware, application code) on separate partitions from frequently-written data (logs, configuration, user data). This separation simplifies updates and optimizes wear patterns.
Reserve space: Leave adequate free space for file system operation, particularly garbage collection. Running a flash file system near capacity severely degrades performance and may accelerate wear.
Redundancy for critical data: Consider storing critical configuration in multiple locations or maintaining previous versions for recovery.
Testing and Validation
File system testing for embedded systems should include:
Power-fail testing: Cut power at random points during representative write activity and verify on restart that the file system mounts and the data satisfies the application's own consistency rules. A relay or electronic load switch under test-script control, cycling continuously for days, exposes failures that a handful of manual pulls never will. Test the production storage part, since the FTL inside a managed device is as likely to be the weak link as the file system above it.
Endurance testing: Model the write volume the product will generate over its service life, then accelerate it. On raw flash, ubinfo and the MTD statistics expose per-block erase counts and reveal wear-leveling failures directly. On managed flash, eMMC devices report a coarse life-time estimate in their extended CSD registers, and many industrial SD cards expose similar health data through vendor commands.
Stress testing: Exercise the system at high volume occupancy, with fragmented free space, and under concurrent access, since these are the conditions in which garbage collection latency spikes and throughput collapses. A device that performs well on a freshly formatted volume tells you almost nothing about how it will behave after two years in the field.
Summary
File system selection for embedded devices balances storage media characteristics, reliability requirements, performance needs, and resource constraints. FAT provides universal compatibility with minimal resources and no protection whatsoever; flash-native file systems such as UBIFS deliver the wear leveling and power-fail safety that unattended equipment requires. The first question to settle is always whether the medium is raw flash or a managed device with its own translation layer, because that single fact eliminates most of the candidates.
Managed flash simplifies selection by handling wear leveling internally, but it does not remove the need to understand flash behavior. Write amplification, garbage collection latency at high occupancy, and the wide quality gap between consumer and industrial parts all still govern how long a product lasts. Flash-friendly designs such as F2FS pay off even on managed media, and discard hints matter regardless of the file system chosen.
For resource-constrained microcontrollers, LittleFS supplies bounded-memory, power-fail-safe storage in a few kilobytes of code, while FatFs remains the answer when a removable card must also be readable on a desktop computer. Read-only images such as SquashFS and EROFS give static content both compression and immunity from corruption, and pairing one with a small writable partition is the standard embedded Linux layout.
Deployment matters as much as selection. Tune mount options, separate static from dynamic data across partitions, leave headroom for garbage collection, and make the application atomic in its own right rather than assuming the file system will cover for it. Validate the result with sustained power-cycle testing on the production storage part and with endurance measurements against the expected service life. File systems chosen and configured this way let embedded devices store data reliably for the full life of the product.
Related Topics
To deepen your understanding of how file systems fit within embedded storage and software design, consider exploring: