Yarn 11 ๐งถ Offline Caching and Mirroring
Every yarn install is a network request away from failure. The npm registry can go down, a corporate proxy can block a scoped package, or a build server can be air-gapped entirely. Yarn’s offline caching and mirroring features exist to eliminate that dependency: once packages are downloaded, they are stored locally and can be reused without touching the network again. In Yarn Berry, this is not an optional optimizationโthe cache is mandatory because PnP reads packages directly from zip archives inside it. The configuration surface is small but full of naming traps: enableGlobalCache and enableMirror do not mean what their names suggest, and confusing them leads to caches that appear local but behave global.
Key point: Yarn Berry stores every package as a zip archive in a cache folder. The default is a project-local cache at .yarn/cache. Setting enableGlobalCache: true moves the cache to the system-wide Yarn folder, sharing it across projects. The offline mirror is this local cacheโnot a separate feature. When you commit .yarn/cache to Git, you have created an offline mirror.
Why offline caching and mirroring exist
The registry availability problem. The npm registry is reliable but not infallible. When it goes down, every project that depends on it fails to install. Companies have traditionally solved this by running a private registry mirror (Verdaccio, Nexus), but that requires infrastructure, configuration, and ongoing maintenance. Yarn’s offline mirror is a simpler alternative: keep the cache inside the repository, and every commit becomes self-installable regardless of registry status .
The PnP dependency problem. Yarn PnP does not extract packages to node_modules. It reads them directly from zip archives. This means the cache is not optionalโit is the source of truth for the dependency tree. If the cache is deleted, PnP cannot function until yarn install rebuilds it . The offline cache and the PnP runtime are inseparable.
The CI reproducibility problem. A CI runner starts with an empty cache. If the registry is slow or rate-limited, every build pays the penalty. A committed cache eliminates that variance: the exact zip archives that were tested locally are the ones installed in CI. The --immutable-cache flag goes further, failing the build if the cache does not match the lockfile, which catches accidental tampering or missing packages .
The naming confusion problem. In Yarn Berry, enableGlobalCache controls whether the cache is local or global. When set to true (the default for new projects that do not commit the cache), Yarn stores packages in a system-wide folder. When set to false, Yarn uses the project-local .yarn/cache folder. The name is counterintuitive: “enable global cache” means “use the shared cache,” not “enable caching at all.” The enableMirror setting is different: it controls whether Yarn also maintains a global mirror of packages even when using a local cache. Disabling the mirror saves disk space but sacrifices the speed of cross-project reuse .
The trade-off. A committed cache increases repository size. The zips are uncompressed (so Git can delta them effectively), and a large project can add hundreds of megabytes. For teams that prioritize absolute reproducibility over repository weight, the trade is worth it. For others, enableGlobalCache: true with no committed cache is the pragmatic default.
a. Configuring the local cache
The cache configuration lives in .yarnrc.yml. The critical setting for offline mirroring is enableGlobalCache: false, which redirects the cache from the system folder to .yarn/cache inside the project.
# .yarnrc.yml
enableGlobalCache: false
After setting this, run yarn install. The .yarn/cache folder is populated with one .zip file per package. The .pnp.cjs loader is generated at the project root. Both must be committed to Git for the repository to be self-contained .
The .gitignore must be updated to un-ignore the cache. Yarn’s default .gitignore excludes .yarn/* and .pnp.*, which would defeat the purpose. The correct pattern uses negation:
# .gitignore
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
!.pnp.cjs
!.pnp.loader.mjs
The ! patterns explicitly include the paths that Zero-Installs requires. Without them, git add .yarn/cache silently does nothing .
b. The global cache and mirror distinction
When enableGlobalCache: true is set (the default for projects that do not commit the cache), Yarn uses a shared cache folder outside the project. The location is <globalFolder>/cache, typically under ~/.yarn/berry/cache on Linux. Packages installed in one project are immediately available in another, reducing download time and disk usage .
The enableMirror setting controls a separate layer: the global mirror. Even when using a local cache, Yarn can maintain a global copy of every downloaded package as an abstraction between the local cache and the network. Disabling it (enableMirror: false) means Yarn only uses the local cache, which is useful in CI where the global mirror adds overhead without benefit .
The practical rule: if you commit the cache, use enableGlobalCache: false and leave enableMirror at its default. If you do not commit the cache, use enableGlobalCache: true to share packages across projects. Mixing the two (local cache with mirror enabled) is valid but adds disk overhead for no reproducibility gain.
c. Verifying and maintaining the cache
The cache is not a write-once artifact. It must stay synchronized with yarn.lock. When dependencies are added, removed, or upgraded, the cache is updated automatically. Yarn prunes unreferenced packages during install, but manual cleanup is possible with yarn cache clean .
In CI, the cache must be validated. The --immutable-cache flag fails the install if the cache would need to be modifiedโeither because a package is missing or because the lockfile references something not present. The --check-cache flag goes further, verifying checksums to detect tampering. For repositories that accept external pull requests, this is essential: a malicious contributor could otherwise modify a zip file in the cache before submitting the PR .
# CI validation
yarn install --immutable --immutable-cache --check-cache
The cache is also sensitive to native dependencies. Packages that compile native code cannot run directly from zip archives. When such a package is added or updated, yarn install must be run to build the native component. Yarn displays an explicit error if this step is forgotten, and the pattern remains viable for the vast majority of pure-JavaScript packages .
Complete Example Session
# ============================================
# PART 1: ENABLE LOCAL CACHE
# ============================================
# .yarnrc.yml
enableGlobalCache: false
# ============================================
# PART 2: INSTALL AND POPULATE CACHE
# ============================================
yarn install
# .yarn/cache/ is populated with package zips.
# .pnp.cjs is generated.
# ============================================
# PART 3: UPDATE GITIGNORE
# ============================================
# .gitignore
.yarn/*
!.yarn/cache
!.yarn/releases
.pnp.*
!.pnp.cjs
!.pnp.loader.mjs
# ============================================
# PART 4: COMMIT THE CACHE
# ============================================
git add .yarnrc.yml .yarn/cache .pnp.cjs .pnp.loader.mjs yarn.lock
git commit -m "Enable offline mirror"
# ============================================
# PART 5: VERIFY ON A FRESH CLONE
# ============================================
git clone <repo> test-offline
cd test-offline
yarn node -e "console.log('offline mirror works')"
# ============================================
# PART 6: CI VALIDATION
# ============================================
yarn install --immutable --immutable-cache --check-cache
# ============================================
# PART 7: SWITCH TO GLOBAL CACHE
# ============================================
# .yarnrc.yml
enableGlobalCache: true
# The local .yarn/cache is no longer used.
# Packages are shared with other projects.
The seven parts trace the lifecycle: configuring the local cache, populating it, updating .gitignore, committing, verifying on a fresh clone, validating in CI, and switching to the global cache if reproducibility is not required.
Quick Reference
Cache Configuration
| Setting | Effect |
|---|---|
enableGlobalCache: false | Local cache at .yarn/cache |
enableGlobalCache: true | System-wide shared cache |
enableMirror: false | Disable global mirror when using local cache |
cacheFolder | Custom local cache path (default .yarn/cache) |
Validation Flags
| Flag | Purpose |
|---|---|
--immutable-cache | Fail if cache would change |
--check-cache | Verify package checksums |
--immutable | Lockfile must not change |
Files to Commit for Offline Mirror
| File/Directory | Purpose |
|---|---|
.yarn/cache/ | Package zips |
.pnp.cjs | PnP loader |
.pnp.loader.mjs | ESM loader |
yarn.lock | Resolutions |
.yarnrc.yml | Configuration |
Best Practices
โ Do This:
enableGlobalCache: false
# Local cache for committed offline mirror
yarn install --immutable --immutable-cache --check-cache
# CI validation
!.yarn/cache
# Un-ignore cache in .gitignore
โ Don’t Do This:
# Use enableGlobalCache: true with committed cache
# The cache is not in the project folder
# Skip --check-cache in CI with external PRs
# Allows cache tampering
# Assume cache survives deletion
# PnP requires the cache to function
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Cache not committed | .gitignore ignores .yarn/* | Add !.yarn/cache negation |
| CI fails with immutable-cache | Cache out of sync | Run yarn install and commit updated cache |
| Native dependency fails | Zip cannot contain compiled binaries | Run yarn install after adding |
| Repository too large | Committed cache is large | Use enableGlobalCache: true or Lazy Installs |
| Registry still contacted | enableGlobalCache: true | Set to false for offline mode |
Real-World Examples
1. Enable Local Cache
enableGlobalCache: false
2. Commit Cache
git add .yarn/cache .pnp.cjs yarn.lock
3. Un-Ignore Cache
!.yarn/cache
4. Validate in CI
yarn install --immutable --immutable-cache --check-cache
5. Check Cache Size
du -sh .yarn/cache
6. Switch to Global
enableGlobalCache: true
7. Disable Mirror
enableMirror: false
8. Clean Cache
yarn cache clean
9. Check Cache Integrity
yarn install --check-cache
10. Fresh Clone Test
git clone <url> && cd <repo> && yarn node -e "console.log('ok')"
Visual
Offline Mirror Repository Contents
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Repository โ
โ โ
โ .yarnrc.yml (enableGlobalCache: false) โ
โ yarn.lock โ
โ .pnp.cjs (loader) โ
โ .yarn/cache/ โ
โ โโโ lodash-npm-4.17.21-abc.zip โ
โ โโโ express-npm-4.18.2-def.zip โ
โ โโโ ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The cache and loader are committed. The project installs without network.
Global vs Local Cache
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ enableGlobalCache: false โ
โ โโโ .yarn/cache/ (project-local) โ
โ โโโ Committed to Git โ
โ โโโ Offline mirror โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ enableGlobalCache: true โ
โ โโโ ~/.yarn/berry/cache/ (system-wide) โ
โ โโโ Not committed to Git โ
โ โโโ Shared across projects โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The local cache is the offline mirror. The global cache is a shared convenience.
Summary
| Item | Value |
|---|---|
| Offline mirror definition | Local cache committed to Git |
| Local cache setting | enableGlobalCache: false |
| Default cache path | .yarn/cache |
| Global cache path | ~/.yarn/berry/cache |
| PnP cache dependency | Mandatory |
| CI validation | --immutable --immutable-cache --check-cache |
| Gitignore pattern | !.yarn/cache |
| Native dependency caveat | Requires yarn install |
Key takeaways:
- The offline mirror is the local cache. Setting
enableGlobalCache: falsestores packages in.yarn/cache. Committing that folder creates the mirror . - PnP cannot function without the cache. Packages are read directly from zip archives. Deleting the cache breaks the project until
yarn installrebuilds it . - The naming is inverted.
enableGlobalCache: truemeans “use the shared system cache.”falsemeans “use the project-local cache” . - CI must validate the cache.
--immutable-cachefails if the cache would change.--check-cacheverifies checksums to detect tampering . - Native dependencies break the pure-offline pattern. Packages with compiled code require
yarn installwhen added or updated . - The global mirror is a separate layer.
enableMirrorcontrols whether Yarn also maintains a system-wide copy even when using a local cache .
Remember: The offline cache is not an optimization in Yarn Berry. It is the runtime. PnP reads from the cache; without it, nothing runs. Committing the cache to Git turns every commit into a self-installable artifact. The configuration is a single setting, but the naming is deceptive: enableGlobalCache: false is what enables the local, committable cache that makes offline installs possible.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!