Yarn Berry Zero-Installs and Git Repository Bloat Management
0 reputation · 11 Dec 2021, 21:54 UTC
0 reputation · 11 Dec 2021, 21:54 UTC
Yarn Berry enables a repeatable development environment through the Zero-Installs strategy, where the offline cache is committed directly to version control. This approach aims to eliminate the need for yarn install during CI/CD pipelines and local onboarding by providing the .pnp.cjs map and cached zip archives.
While this ensures deterministic builds and immediate startup, committing the entire cache can lead to significant repository growth. There is a design tension between maintaining a fully self-contained environment and avoiding an oversized Git history that slows down cloning and fetching operations.
What are the recommended strategies for balancing Zero-Installs with repository size constraints? Should specific cache patterns be ignored in favor of Git LFS, or is there a configuration to prune the committed cache without breaking the PnP mapping?
Short answer: keep Zero-Installs, but regenerate the cache with compressionLevel: 0 and let Yarn's built-in pruning do the rest. Do not move .yarn/cache to Git LFS, and never hand-delete individual archives — the PnP map and lockfile reference each entry by checksum, so removing one file breaks every subsequent install.
Zero-Installs works because .pnp.cjs resolves every package to a specific archive in .yarn/cache, identified by checksum. There is no supported "prune the committed cache" command beyond yarn install itself — and that's fine, because Yarn already removes archives that are no longer referenced by the lockfile during each install. The committed cache therefore reflects only your current dependency set. If it feels bloated, the driver is your dependency tree, not leftover files.
By default Yarn stores cache archives as compressed zips. Compressed binaries don't delta-compress in Git, so every dependency update adds roughly the full archive size to history. Setting the compression level to 0 stores the archives uncompressed:
yarn config set compressionLevel 0
yarn install # regenerates the whole cache consistently
The working tree gets larger, but the stored files are now plain, diffable content, so Git can delta-compress them across commits. Over the life of a repository this typically produces a smaller history than the default compressed cache. Commit the regenerated cache in a single commit — mixing formats creates noisy diffs.
LFS pointer-izes the archives, which defeats the core promise of Zero-Installs: a fresh clone that runs without an install step. Every clone and CI job now needs LFS smudging configured, and since zip archives don't delta-compress anyway, LFS buys you little beyond pushing the bytes to a second store. It also adds a failure mode (missing LFS objects) to your most reliability-sensitive path.
The legitimate escape hatch is to abandon Zero-Installs selectively: gitignore .yarn/cache while still committing .pnp.cjs, .yarn/releases, and the lockfile, then run yarn install --immutable in CI (ideally with a warmed global cache). You keep PnP's strictness and deterministic resolution but trade away clone-and-run onboarding. This is a real trade-off, not a free optimization.
yarnPath (yarn set version) so contributors don't generate divergent cache diffs.yarn dedupe periodically — dependency count and duplication drive cache size.du -sh .yarn/cache
git count-objects -vH
yarn config get compressionLevel
Measure before and after the compression change. Then clone the repo fresh into a temp directory and run your build without yarn install to confirm Zero-Installs still works, and add yarn install --immutable --immutable-cache to CI to guarantee the committed cache matches the lockfile exactly.
One caveat: compressionLevel behavior and defaults vary across Yarn Berry releases — check the version pinned in .yarnrc.yml before assuming a default. And avoid rewriting Git history to purge an old bloated cache unless the bloat is severe; a forward-fixing regeneration is almost always the safer path.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.