Open-source project
littlefs-project/littlefs avatar
littlefs-project/littlefs

littlefs: a fail-safe filesystem for microcontrollers, and what it asks of your block device

A little fail-safe filesystem designed for microcontrollers

7,013 stars1,054 forksCBSD-3-Clause

At a glance

What is it?
littlefs is a C99 block-based filesystem with power-loss resilience, dynamic wear leveling and bounded RAM, aimed at firmware engineers working with flash. The catch is that you supply the block device layer.
Who is it for?
littlefs fits firmware projects where the storage is raw flash, RAM is measured in kilobytes, and the device can lose power at any instant. On storage with its own flash translation layer, assess whether littlefs's power-loss behavior and other features justify the extra layer.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • littlefs-project/littlefs7,013 ★ · +66
Star history
Repository2026-09-232026-11-14
littlefs-project/littlefs6.9k7k

66 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What littlefs solves, and the firmware engineers it targets

Microcontrollers write to flash, and flash has two properties that make naive writing dangerous. Erases happen in blocks, writes happen in pages, and a power failure in the middle of either leaves storage in a state the previous code did not expect. The README describes littlefs as a little fail-safe filesystem designed for microcontrollers, and the three properties it advertises are the answer to that situation: power-loss resilience, dynamic wear leveling, and bounded RAM and ROM.

The audience is narrow and specific. You are writing C99 firmware, you have a raw block device rather than a filesystem already mounted on top of it, and you care about what happens when the battery is pulled mid-write. The README states that all file operations have strong copy-on-write guarantees and that if power is lost the filesystem falls back to the last known good state. That is a claim about the design, not a guarantee about your driver: littlefs can only fall back to a good state if the block device underneath it behaves.

Bounded RAM is the other half of the pitch. RAM usage is strictly bounded, so consumption does not change as the filesystem grows, and the README notes there is no unbounded recursion. Dynamic memory is limited to configurable buffers, which the user can provide statically. For a device with a fixed heap, that predictability matters more than throughput.

The two-layered design: metadata pairs and copy-on-write file data

The Design section of the README describes the structure as a two-layered cake. Small logs, called metadata pairs, store metadata and provide fast updates to it anywhere on storage. Larger copy-on-write structures store file data compactly, and the README says this avoids wear amplification cost on the data path. Both structures are built out of blocks fed by a common block allocator. By limiting the number of erases allowed on a block per allocation, the allocator provides dynamic wear leveling across the whole filesystem.

The consequence is that metadata and file data have different update behaviour, and the README is explicit about where durability lands: file updates are not committed to the filesystem until sync or close is called on the file. If your application writes a record and never closes or syncs the file, a power loss can discard it, and that is the design working as intended rather than a bug.

All POSIX operations, including remove and rename, are atomic even on power loss, according to the README. The API is POSIX-like with one deviation the README calls out: the allocation of filesystem structures must be provided by the user. State lives in an lfs_t that you allocate, so several filesystems can be mounted at once.

Wiring up the block device: read, prog, erase, sync

littlefs does not talk to hardware. You fill in a struct lfs_config with four function pointers, and the README's example names them user_provided_block_device_read, prog, erase and sync, alongside dimensions such as read_size, prog_size, block_size, block_count, cache_size, lookahead_size and block_cycles. The dimensions are not decoration: they trade memory for performance, and they must match the part.

The sync callback is the one most likely to be implemented incorrectly. The README states that if your storage caches writes, sync must flush all data to memory and ensure the next read fetches from memory, otherwise data integrity cannot be guaranteed. If the write function does no caching and every read or write hits memory directly, sync can simply return 0. A driver that returns 0 while a write-back cache still holds dirty pages is the failure mode to look for during bring-up.

Error handling has a similar shape. Every littlefs call can return a negative error code, either from enum lfs_error in lfs.h or from your own block device functions. The README also notes that prog and erase may return LFS_ERR_CORRUPT if your implementation can already detect corrupt blocks, but wear leveling does not depend on that return code: all data is read back and checked for integrity. Bad blocks are detected and worked around, but the detection does not come free.

Building littlefs and running the boot counter example

The repository is a source tree, not a package. The top level contains lfs.c, lfs.h, lfs_util.c, lfs_util.h, a Makefile, and the tests, benches and runners directories. The README points at lfs.h for the detailed documentation, describing it as at least as much detail as is currently available. The practical path is to drop the two source files into your build and compile them as C99.

The Makefile chooses its target by what it finds in the directory. If test.c or main.c is present it builds $(BUILDDIR)/lfs, otherwise it builds $(BUILDDIR)/liblfs.a. To exercise the filesystem on a host you need a block device implementation and an entry point, and the README's example shows the configuration struct that ties a block device to littlefs:

c
const struct lfs_config cfg = {
    .read  = user_provided_block_device_read,
    .prog  = user_provided_block_device_prog,
    .erase = user_provided_block_device_erase,
    .sync  = user_provided_block_device_sync,
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 128,
    .cache_size = 16,
    .lookahead_size = 16,
    .block_cycles = 500,
};

Those four function pointers are yours to write. With the lfs_t and the configuration struct, you can format a block device or mount the filesystem. The README presents a boot counter as a power-loss example, but an interruption before a successful close can lose the latest increment. Its demonstration also formats after any mount error; production firmware should distinguish unformatted media from I/O errors and corruption before formatting, to avoid erasing recoverable data.

Where littlefs is the wrong choice, and what it does not do for you

The first limitation is architectural. littlefs assumes it owns a block device and that you can describe that device in terms of read, prog and erase units. If your storage sits behind a controller with its own flash translation layer and wear leveling, you are stacking two allocators with different assumptions about block lifetime, and the wear leveling littlefs provides is doing work the layer below already did.

The second is that the fail-safe property means falling back to the last known good state on power loss, not recovering a previous revision. If you need transactional multi-file updates with an explicit rollback path, littlefs gives you atomic individual operations and leaves the transaction design to you.

The third is memory and API shape. Filesystem structures must be user-allocated, and the caches and lookahead buffers come out of your budget; static buffers are optional if you want to avoid dynamic memory. Check the comments in lfs.h, which the README names as the detailed documentation, for how concurrency is handled before sharing an lfs_t between threads.

Finally, the documentation situation is thin by the project's own account. The README points to comments in lfs.h, qualified as at least as much detail as is currently available. The repository carries DESIGN.md and SPEC.md, which is more than many embedded libraries offer, but there is no separate manual.

littlefs vs SPIFFS: different answers to the same flash problem

The comparison people reach for is SPIFFS, and the difference is structural rather than a matter of tuning. littlefs splits the problem: metadata pairs are small logs for fast metadata updates, while file data lives in copy-on-write structures that the README says store data compactly without wear amplification cost.

The practical divergence is in RAM and in predictability. littlefs advertises strictly bounded RAM that does not grow with the filesystem, and a block allocator that caps erases per block per allocation to spread wear. littlefs also detects bad blocks and works around them, and it checks all data for integrity on read-back rather than trusting the return code of prog and erase.

If you are updating small records in place on a device that loses power, littlefs's copy-on-write metadata path is the more defensible design.

Editorial conclusion

littlefs fits firmware projects where the storage is raw flash, RAM is measured in kilobytes, and the device can lose power at any instant. On storage with its own flash translation layer, assess whether littlefs's power-loss behavior and other features justify the extra layer. File updates require a successful sync or close for the documented commit behavior. Before adopting it, verify three things against your own hardware: that your sync callback really flushes a write-back cache, that your block_size and prog_size match the part's erase and program units, and that your RAM budget covers the caches and lookahead buffers you configure. The example in the README with read_size, prog_size and cache_size at 16 bytes and block_size at 4096 is a starting point for that budget, not a measured requirement.

Frequently asked questions

What does littlefs do?

It is a block-based filesystem written in C99 for microcontrollers, providing POSIX-like file and directory operations on top of a raw block device. The README describes it as fail-safe, with copy-on-write guarantees on all file operations and a fallback to the last known good state after power loss.

What are the key differences between SPIFFS and littlefs?

littlefs uses small metadata logs for fast metadata updates and copy-on-write structures for file data, with a block allocator that limits erases per block per allocation to provide dynamic wear leveling. It also advertises strictly bounded RAM that does not change as the filesystem grows, and it reads data back to check integrity rather than trusting the return code of prog and erase.

How do you use littlefs?

You provide a struct lfs_config with read, prog, erase and sync callbacks plus block device dimensions, allocate an lfs_t, then call lfs_mount or lfs_format. After that, littlefs provides POSIX-like file and directory functions, and file updates are not committed until sync or close is called on the file.

Is littlefs thread safe?

The README's usage notes do not cover thread safety; it points to the comments in lfs.h for detailed documentation, so check there. State lives in the lfs_t that the user allocates, so any concurrency protection is up to the integration around it.

What is lfs.h used for in littlefs?

The README points to lfs.h for detailed documentation, describing it as at least as much detail as is currently available. It also defines the error codes, since every littlefs call can return a negative value from enum lfs_error or from your own block device functions.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. littlefs-project/littlefs on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/littlefs-project-littlefs.svg)](https://hysenlabs.com/projects/littlefs-project-littlefs)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/littlefs-project-littlefs.svg)](https://hysenlabs.com/projects/littlefs-project-littlefs)

Was this useful?