claude-cellar
Transparent zstd compression for Claude Code session files via a FUSE filesystem.
Claude Code stores every session as a plain
JSONL file under ~/.claude/projects/**/. Over time these accumulate — hundreds
of MB of text that compress down to ~18% of their size with zstd -19.
claude-cellar v0.2 mounts a FUSE filesystem on ~/.claude/projects/ so
that Claude sees regular .jsonl files while the real bytes live as
.jsonl.zst in a separate store directory. Decompression happens lazily
on open() into a per-FD tmpfs scratch buffer; recompression happens on
the last close() if the FD was written to. Multi-claude is correct by
construction (every FD is kernel-isolated; there is exactly one daemon
serving the mount).
Verified compression on real sessions: ~82% size reduction (zstd -19, hash-verified round-trip). Decompression on open is sub-second for typical sessions.
Install (Linux)
From crates.io
That's it. After install, claude works as always — the FUSE daemon
is up, mounted at ~/.claude/projects/, and started automatically at
every login.
From source
Migration from v0.1.x
# If you used v0.1's install (which created a ~/.local/bin/claude shim):
# Move existing compressed sessions into the new store layout:
# Install the v0.2 daemon:
What you don't have to do
- Run
claude-cellar mountmanually. systemd does it. - Decompress sessions before resuming. The FUSE mount makes them appear as
regular
.jsonl. - Worry about multiple
claudeinstances stepping on each other. There is one daemon, every FD is kernel-isolated. - Configure anything. Defaults work out of the box.
Architecture
~/.claude/projects/ ← FUSE mount (virtual)
│
▼
[ claude-cellar daemon ]
│ │
decompress on open() re-compress on release()
│ │
▼ ▼
$XDG_RUNTIME_DIR/claude-cellar/ ~/.local/share/claude-cellar/store/
scratch/ (compressed .jsonl.zst)
(per-FD tmpfs)
| Path | Default | Override env var |
|---|---|---|
| Mount | ~/.claude/projects/ |
CLAUDE_CELLAR_MOUNT_DIR |
| Store | ~/.local/share/claude-cellar/store/ |
CLAUDE_CELLAR_STORE_DIR |
| Scratch (tmpfs) | $XDG_RUNTIME_DIR/claude-cellar/scratch/ |
CLAUDE_CELLAR_SCRATCH_DIR |
| Log | $XDG_STATE_HOME/claude-cellar/cellar.log |
— |
| Config | $XDG_CONFIG_HOME/claude-cellar/ |
— |
You can put the store on any filesystem (local, NFS, encrypted volume).
Scratch should stay on tmpfs (default). The mount is fixed at
~/.claude/projects/ because that's what Claude looks at.
Commands
Day-to-day, you run claude (not claude-cellar). The CLI is here for
maintenance and inspection:
Why FUSE (and not a wrapper)
v0.1 used a cellar run wrapper that hydrated every .jsonl.zst to
.jsonl before exec'ing claude, then re-compressed on exit. Two
structural problems:
- Transient disk doubled during a session: ~25 MB of compressed
sessions decompressed to ~125 MB on disk for the duration of every
claudeinvocation. - Multi-claude data loss: with two
claudeinstances open, the first wrapper to exit ran cleanup over files the others were still writing.
v0.2 sidesteps both by moving the boundary to the kernel: compressed files stay compressed in the store; only the currently open sessions are decompressed (in tmpfs); every FD is independent.
Configuration
All env vars optional. Defaults work out of the box.
| Var | Purpose |
|---|---|
CLAUDE_CELLAR_STORE_DIR |
Override the store location (default ~/.local/share/claude-cellar/store). Useful for NFS-shared layouts. |
CLAUDE_CELLAR_MOUNT_DIR |
Override the mount location (rare; default ~/.claude/projects). |
CLAUDE_CELLAR_SCRATCH_DIR |
Override the scratch dir (default $XDG_RUNTIME_DIR/claude-cellar/scratch). |
CLAUDE_CELLAR_MAX_FDS |
Cap on simultaneous open FDs through the FUSE (default 16). |
CLAUDE_CELLAR_CLAUDE_BIN |
Explicit path to the real Claude binary (used by resume). |
Compatibility
| Claude Code setting / env / flag | Effect on cellar |
|---|---|
Default install (~/.claude/projects/) |
Works out of the box. |
CLAUDE_CODE_SKIP_PROMPT_HISTORY=1 |
No .jsonl is written; FUSE sees nothing; no-op. |
--no-session-persistence (per-run) |
That run does not persist; cellar ignores it. |
cleanupPeriodDays in settings.json |
Claude prunes via the FUSE; cellar deletes the matching .zst and sidecar. |
Platform support
- Linux x86_64 / aarch64: first-class. v0.2 is the recommended path.
- macOS / Windows: not supported in v0.2 (FUSE adapters not yet shipped). Stay on v0.1.x; it still works for single-instance use.
Benchmarks
Measured on three real Claude Code sessions (zstd 1.5.7, single archive pass):
| Session size | gzip -9 | zstd -3 | zstd -19 | xz -6 |
|---|---|---|---|---|
| 670 KB | 17.3% | 13.6% | 11.6% | 11.1% |
| 1.9 MB | 19.5% | 16.9% | 15.0% | 14.6% |
| 4.6 MB | 23.9% | 20.3% | 18.1% | 17.8% |
zstd -19 lands within 0.3 points of xz -6 on ratio and decompresses ~20×
faster (important on the open() path). zstd -19 is the default.
License
Licensed under either of:
- MIT License (LICENSE-MIT or https://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.