We moved JuiceFS metadata from Postgres to Redis and file ops got 7x faster
· the plori team
Migration and measurements: 2026-06-21, on juicefs 1.3.1. Still running on Redis in production as of publication, 2026-07-24, now on JuiceFS CE 1.4.0.
TL;DR. On our production JuiceFS filesystem, one create-plus-rename pair cost about 22ms with Postgres as the metadata engine and about 3.2ms after moving metadata to Redis: roughly 7x, measured during the migration on 2026-06-21. Our first hypothesis, batching the fsync, bought nothing, because JuiceFS serializes metadata mutations and fsync was nearly free. The migration itself was
juicefs dumpandjuicefs loadplus validation. The operational half is the part vendor benchmarks skip: this Redis becomes the filesystem's source of truth, so it runs withnoeviction, AOFeverysecplus RDB snapshots, andmaxmemoryset below the container memory limit; the capacity ceiling is RAM, not disk, at about 400 bytes per inode.
Why milliseconds per file operation matter to us
plori is a cloud AI agent. Each agent has its own computer whose disk is a per-agent FUSE mount over JuiceFS, and agents sleep when idle, which stops billing. Agents spend their lives doing exactly the workload network filesystems hate: cloning repos, scaffolding projects, writing many small files.
Every file operation on that disk crosses FUSE, then our storage plane's policy and lease checks, then a JuiceFS metadata transaction. We had already measured per-operation latency on this path at roughly 100 to 600 times local disk, even though throughput was fine. At those multipliers, whatever the metadata engine charges per operation gets paid on everything an agent does.
Our chunk-write path makes it concrete: a write creates a temporary object and renames it into place, so one logical write pays for a create and a rename. On the loaded production filesystem with Postgres as the metadata engine, that was about 12ms for the create plus about 10ms for the rename: roughly 22ms per pair. Do the arithmetic on any real task: a job that writes 1,000 chunks spends about 22 seconds in metadata alone. After the migration the same pair costs about 3.2 seconds per thousand. That difference is user-visible in an interactive agent.
Why didn't batching fsync help?
Our first hypothesis was the classic one: fsync amplification. Batch the fsyncs, maybe run chunk writes concurrently, collapse the sync cost. We measured it on the live mount and it bought nothing.
The reason is JuiceFS's design: metadata mutations are serialized through the metadata engine, and under writeback the fsync itself was nearly free. There was no sync storm to batch away. The cost was the per-mutation round-trip to the metadata engine, multiplied by however many mutations the workload issues. And our engine was Postgres, which JuiceFS's own guidance places at the slow end of the supported engines; their materials (for example "Introducing Redis as the JuiceFS metadata engine") consistently show Redis a multiple faster than the SQL engines.
We are writing the dead end down deliberately. "Batch the fsync" is the intuitive fix for slow file creates, it is what we tried first, and on JuiceFS it does not work, because fsync was never where the time went.
How much faster is the Redis metadata engine?
We ran a head-to-head that isolated the engine: same juicefs binary (1.3.1), same node, same storage backend, only the metadata engine differing. Measured 2026-06-21:
| Operation | Postgres | Redis | Speedup |
|---|---|---|---|
| create + rename | ~13.9 ms | ~3.2 ms | ~4.4x |
| create only | ~5.4 ms | ~0.92 ms | ~5.9x |
| rename only | ~9.1 ms | ~2.2 ms | ~4.1x |
On the real, loaded production filesystem the delta was larger: a create-plus-rename pair went from 22 ms to 3.2 ms, about 7x. Note what that gap between 13.9 and 22 is telling you: a clean-room SQL engine benchmarks better than the same engine under production load, while Redis measured the same 3.2 ms in both settings. The speedup also applies to all metadata operations (stat, lookup, list), not just the write path.
Two honest framings for these numbers. First, ~7x is our workload's number, create-plus-rename pairs on our filesystem, not a universal constant; JuiceFS's own generic benchmarks put Redis at a smaller multiple over SQL engines, and the extra factor here came from how loaded our Postgres was. Second, 3.2 ms is still an eternity next to a local SSD. The migration moved the floor; it did not repeal the physics. We still design agent state around per-operation cost (pack state into one archive instead of mirroring many small files), and we still keep latency-critical databases off the FUSE plane entirely.
Is Redis safe as a filesystem's metadata store?
This is the question that actually decides the migration, and it is the half the benchmark posts skip. After the cutover, Redis is not a cache in your stack. It is the source of truth for the filesystem's structure. If this Redis loses data, the filesystem loses files, regardless of how intact the object storage underneath is.
Our production configuration, with the reason for each line:
maxmemory-policy noeviction. Non-negotiable. Any eviction policy on filesystem metadata is silent data loss with a cache's name on it.appendonly yeswithappendfsync everysec, plus RDB snapshots, on a persistent volume. The AOF bounds crash loss to about one second of the most recent metadata mutations; RDB is the coarse backstop behind it.maxmemoryset below the container's memory limit. Withnoeviction, filling Redis makes writes fail with a clean OOM write error: metadata mutations error out, reads keep working, and the filesystem degrades loudly but safely. If instead the container limit is the first ceiling you hit, the kernel OOM-kills your metadata engine mid-write. Choose the failure you want ahead of time.- Periodic metadata dumps to object storage (JuiceFS's automatic backup-meta), as defense in depth behind the AOF and RDB.
The durability trade-off versus Postgres should be stated plainly: this is a single Redis instance with everysec fsync, so a crash can lose up to about one second of metadata mutations. Postgres gave us synchronous durability. We took that trade knowingly, for a large latency win on every operation, with the AOF window as the cost. If your workload cannot tolerate that window, a SQL engine or TiKV is the correct choice, and JuiceFS supports both.
What is the capacity ceiling?
RAM, not disk. We measured about 400 bytes of Redis memory per inode on our filesystem. That prices out simply: ten million inodes is on the order of 4 GB of metadata. Watch used_memory, alert well before maxmemory, and remember that the chunk data itself never touches Redis; only the metadata lives there, while file contents stay on object storage.
The scale-out path is also worth naming before you need it: JuiceFS supports TiKV as a metadata engine, which is the standard exit once a filesystem outgrows what one Redis node's memory can hold, at the price of higher per-operation latency than Redis. For a filesystem in the millions of inodes, single-node Redis with headroom is the simple, fast option.
How do you migrate JuiceFS metadata from Postgres to Redis?
JuiceFS ships the mechanism: juicefs dump serializes the entire metadata tree from one engine, juicefs load restores it into another. The migration is non-destructive; the old engine's data stays intact underneath you, which is also your rollback.
# quiet window: stop writers first
juicefs dump "postgres://.../juicefs_meta" meta-dump.json
juicefs load "redis://.../1" meta-dump.json
juicefs status "redis://.../1" # then validate before cutting over
The steps that matter:
- Take a quiet window. Dump with writers stopped, so the dump is a consistent snapshot.
- Validate before cutover, not after. We compared inode and counter totals between the engines, then mounted against the new engine and read back known files end to end through to object storage.
- Cut over by flipping the metadata URL and remounting.
- Keep the old engine's data untouched until you have run against Redis long enough to trust it. Rollback is the same dump/load in reverse, or simply pointing back, if nothing has been written since.
For placement we used the in-cluster Redis, with metadata on its own logical database, separate from the application cache. Filesystem metadata sharing a Redis with app data is fine; sharing a database number is not, if only because FLUSHDB habits and eviction settings are per-instance, not per-database, and you want the instance-level settings dictated by the filesystem.
The config drift trap that almost bit us later
The sneaky failure mode is not the migration. It is configuration management afterwards. Our deployment chart's default metadata URL is not the production Redis, so any upgrade applied without explicitly carrying the live value would silently point JuiceFS back at the old Postgres engine. The resulting filesystem mounts fine and serves a weeks-old universe, which is a far more confusing failure than an error.
Two rules came out of that near-miss. After an engine migration, the metadata URL must be an explicit, always-passed deploy value, never a chart default. And prod config changes get gated on a rendered diff against what is actually live, so a reverted URL shows up as a diff line instead of as a mystery.
Limitations
- All numbers are from 2026-06-21 on juicefs 1.3.1, on our workload and our hardware. We have since upgraded to JuiceFS CE 1.4.0 (2026-07-17); the Redis metadata format was unchanged by that upgrade, and we have not re-run the head-to-head on 1.4.0.
- ~7x compares a loaded production Postgres against Redis on the same filesystem. Your multiple depends heavily on how loaded your SQL engine is; the clean-room multiple we measured was ~4.4x.
- ~400 bytes per inode is our filesystem's average; directory shapes and name lengths move it.
- Single-instance Redis with AOF
everysecaccepts a crash window of up to about one second of metadata mutations. If that is unacceptable, use a SQL engine or TiKV.
FAQ
Is Redis a supported JuiceFS metadata engine? Yes. Redis is one of JuiceFS's officially supported metadata engines, and JuiceFS's own materials position it as the low-latency option, with SQL databases and TiKV as the alternatives with different durability and scale trade-offs.
How much memory does JuiceFS metadata use in Redis? About 400 bytes per inode on our production filesystem, so ten million inodes is on the order of 4 GB. Only metadata lives in Redis; file contents stay on object storage. RAM is the capacity ceiling, not disk.
What happens when Redis hits maxmemory with noeviction?
Writes fail with a clean OOM write error: metadata mutations (creates, renames) error out while reads keep working. Set maxmemory below the container or host memory limit so you hit this loud, recoverable failure instead of the kernel OOM killer taking down the metadata engine.
Can you migrate JuiceFS metadata back from Redis to Postgres?
Yes. The same juicefs dump and juicefs load procedure works in either direction. Keep the old engine's data untouched until validation passes on the new one; it doubles as your rollback.