| |

Yarn 11 ๐Ÿงถ Offline Caching and Mirroring

โฑ๏ธ Estimated read: 9 min | |

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

SettingEffect
enableGlobalCache: falseLocal cache at .yarn/cache
enableGlobalCache: trueSystem-wide shared cache
enableMirror: falseDisable global mirror when using local cache
cacheFolderCustom local cache path (default .yarn/cache)

Validation Flags

FlagPurpose
--immutable-cacheFail if cache would change
--check-cacheVerify package checksums
--immutableLockfile must not change

Files to Commit for Offline Mirror

File/DirectoryPurpose
.yarn/cache/Package zips
.pnp.cjsPnP loader
.pnp.loader.mjsESM loader
yarn.lockResolutions
.yarnrc.ymlConfiguration

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

PitfallWhy It HappensFix
Cache not committed.gitignore ignores .yarn/*Add !.yarn/cache negation
CI fails with immutable-cacheCache out of syncRun yarn install and commit updated cache
Native dependency failsZip cannot contain compiled binariesRun yarn install after adding
Repository too largeCommitted cache is largeUse enableGlobalCache: true or Lazy Installs
Registry still contactedenableGlobalCache: trueSet 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

ItemValue
Offline mirror definitionLocal cache committed to Git
Local cache settingenableGlobalCache: false
Default cache path.yarn/cache
Global cache path~/.yarn/berry/cache
PnP cache dependencyMandatory
CI validation--immutable --immutable-cache --check-cache
Gitignore pattern!.yarn/cache
Native dependency caveatRequires yarn install

Key takeaways:

  • The offline mirror is the local cache. Setting enableGlobalCache: false stores 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 install rebuilds it .
  • The naming is inverted. enableGlobalCache: true means “use the shared system cache.” false means “use the project-local cache” .
  • CI must validate the cache. --immutable-cache fails if the cache would change. --check-cache verifies checksums to detect tampering .
  • Native dependencies break the pure-offline pattern. Packages with compiled code require yarn install when added or updated .
  • The global mirror is a separate layer. enableMirror controls 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.

Was this helpful?


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!