Sealed directories shrink the filer’s metadata store by packing a cold directory’s entries into a compressed segment in a volume, leaving one manifest behind — so the same filer holds one to two orders of magnitude more files. The catch, in the first post on the feature, was the word cold. The default frozen seal is fully read-only: every write is rejected until you unseal.
But “cold” is rarely absolute. A log directory is done — except for the straggler that arrives a day late. Last quarter’s dataset is final — until someone files a correction. The deep leaves of a hash-fanout tree are written once — mostly. Freezing those means unsealing, writing, and re-sealing every time, or just leaving them unsealed and paying full metadata price forever.
Mutable seals are the answer: seal a directory but keep it writable. This post shows how to turn one on, then measures exactly what it costs — savings, write latency, and compaction — on a live single-node cluster, and closes with the question everyone asks next: can I just seal everything this way?
One flag makes it writable
A normal seal, and a mutable one, differ by a single flag:
weed shell> fs.seal /data/logs/2026 # frozen: read-only until unsealed
weed shell> fs.seal -allowUpdates /data/logs/2026 # mutable: writes stay admitted
The output is the same either way — the directory’s entries repack into a segment:
/data/logs/2026: sealed 5000 entries into 1 segments (139.83 KiB)
sealed 1 directories, 5000 entries, 139.83 KiB of manifests
The mode is a property of the seal, and it flips in place — re-run fs.seal with or without -allowUpdates to switch a directory between frozen and mutable, no unseal required. In a standing auto_seal policy, the same choice is one key per rule:
{ "pattern": "/data/logs/**", "idleSeconds": 7776000, "allowUpdates": true, "compactMinRows": 500 }
How a mutable seal stays cheap
The trick is that the bulk of the directory stays packed. Writes don’t unpack it — they land beside it as small overlay records, and a background compaction periodically folds them back in.
That’s the whole model. A reader sees a normal directory (the filer merges the manifest with the overlay). A writer sees a normal directory too. The only difference from an unsealed directory is the metadata footprint — and that’s exactly what we measured.
The trade-off, measured
Everything below is from a live single-node cluster (a 2-vCPU Debian VM). The test directory holds 5,000 small files, and we read the filer store’s on-disk size directly at each step.
Savings degrade one write at a time
Sealed with no writes, the 5,000 entries collapse to a single manifest — the entries themselves repack into a 139.83 KiB segment that lives in a volume, not the filer store. Then, as writes arrive, each one adds a single overlay row back:
The numbers were strikingly clean. Writing overlay files in batches, the filer store grew by 153 bytes per overlay row, run after run — essentially identical to the 150 bytes an ordinary unsealed entry costs. That’s the whole story of the mutable trade-off in one measurement:
A mutable seal costs the full frozen manifest plus one ordinary store row for every changed entry that hasn’t been compacted yet.
So the savings don’t cliff — they erode gracefully. Seal a 5,000-entry directory that then takes 50 writes, and you’re still 99% packed. Let 2,000 writes pile up (40% of the directory), and you’ve handed back 40% of the row savings — until the next compaction folds them in and resets you to a single manifest.
Writing to a sealed directory is cheap — usually
A frozen seal rejects writes; a mutable seal admits them. How much does admitting them cost? We timed 200 sequential creates into a plain directory and into the mutable sealed directory:
| 200 sequential creates | wall time |
|---|---|
| Plain directory | 2.92 s |
| Mutable sealed directory | 2.84 s |
Indistinguishable. A create whose name sorts outside the sealed segment’s range — a new day’s log file, an incrementing ID — never touches the packed segment at all; the filer just checks a small in-memory index and writes an ordinary row. (Both figures are dominated by per-request client overhead on this box; the point is the equivalence, not the absolute number.)
The one case that costs more is a write whose name falls inside the packed range — an overwrite or delete of an existing member, or a create that could collide with one. The first such touch has to fetch and decompress the covering segment to check membership. The technical reference measures that cold touch at ~0.9 ms (versus ~67 µs for a plain create), dropping back to ~150 µs once the segment is cached. So in-range churn pays a one-time, per-segment decompression tax; everything else is free.
Compaction is a fixed cost, not a growing one
When the overlay grows past a threshold (compactMinRows, default 1024, or compactPercent, default 10% — whichever comes first) and the directory has gone quiet, a compaction re-seals it: it rebuilds a fresh manifest that already includes every overlaid change and clears the overlay. It’s the exact same crash-safe state machine as the original seal.
We timed it. Folding 2,000 overlay writes into the 5,000-entry directory took 6.08 s. For comparison, a fresh seal of the same directory (now ~7,200 entries) took 6.03 s — barely different:
| Operation | wall time |
|---|---|
| Compaction (fold 2,000 overlay rows into a 5,000-entry directory) | 6.08 s |
| Fresh seal of the ~7,200-entry directory | 6.03 s |
Compaction cost tracks the total directory size, not how much overlay accumulated — and even that is mostly a fixed ~5.5-second settle delay built into the seal fence (so peer caches and in-flight writes drain safely) rather than the rebuild itself. At this scale the fence dominates; the rebuild stays cheap until directories get much larger. Crucially, the directory stays readable and writable throughout: compaction is a background re-seal, not a lock.
What mutable seals are for
Put together, the measurements point at one profile: data that is mostly settled but not entirely done.
- A log or ingest directory that’s closed except for rare late arrivals — writes land outside the packed range, so they’re free, and the overlay stays tiny.
- A dataset or experiment that occasionally takes corrections — a handful of in-range overwrites between long quiet periods, folded away by the next compaction.
- The drifting leaves of a hash-fanout tree — mostly cold, a few entries changing, none of it worth keeping unsealed at full metadata price.
For data that is genuinely finished, prefer a frozen seal: it needs no compaction cycles at all, and it gives the strongest guarantee — a mount client can cache its listing forever, knowing nothing under it will ever change.
So… can I just seal everything mutable?
It’s tempting. Mutable seals are transparent and reversible, writes still work, so why not point a rule at / and seal the whole namespace mutable? The measurements say no — and show exactly why.
The savings chart above is also the anti-pattern chart. On a hot directory, writes don’t trickle — they pour. The overlay climbs toward the entry count as fast as you write, so the filer-store footprint climbs right back toward unsealed. Compaction fires, spends its ~6-second fence, and the overlay immediately refills. You get the cost of sealing — the compaction churn, the in-range decompression tax on overwrites — with none of the benefit, because nothing stays packed. A mutable seal only pays off when writes are the exception, not the rule.
This is why auto_seal never seals a directory that’s under active write in the first place: its rules gate on an idle window measured from the child files’ modification times. A directory has to have gone quiet — 30 days, 90 days, whatever your rule says — before it’s sealed at all, and a mutable one has to go quiet again before it’s compacted. The policy is built to seal the cold tail and leave the hot working set alone. Sealing everything mutable by hand just defeats that gate.
A simple way to hold the two modes in your head:
- Frozen — finished data. Maximum savings, zero maintenance, strongest guarantee. The wrong choice only if a write ever needs to land.
- Mutable — mostly-cold data with a trickle of writes. Nearly the same savings, self-healing via compaction. The wrong choice if the directory is actually hot.
- Unsealed — hot data. If it changes constantly, it belongs in the filer store, full price. That’s what the store is for.
Sealing is about moving the cold majority of a large namespace out of the way so the filer can serve far more of it. Mutable seals widen “cold” to include “cold enough” — the directories that are done except for the occasional straggler — without giving up the savings. They’re not a license to seal the parts that are still busy.
Read the Sealed Directories overview and the technical reference for the full mechanics, or get started — SeaweedFS Enterprise is free for development and testing under 25TB.