Topics
Recent articles

Pnpm Shared Store and Isolated Worktrees

Pnpm shares package contents between separate installs through a global content-addressable store, but each project maintains its own dependency graph and node_modules structure.

Table of Contents7 sections
Close-up of hands typing on a laptop with code on screen, perfect for work from home and tech themes.
Close-up of hands typing on a laptop with code on screen, perfect for work from home and tech themes.

When working with multiple git worktrees and concurrent builds, understanding what gets shared globally versus what must remain isolated is crucial to prevent mysterious build failures.

Quick solution:

export PNPM_HOME="$HOME/.local/share/pnpm"
export CI=true
pnpm install --frozen-lockfile

Prerequisites: Ensure your global store path is writable and that each worktree maintains an independent lockfile and local build cache. Cautions: Never point two concurrent tasks at the same unisolated output directory or allow concurrent writes to shared build artifacts without locking.

What does pnpm share between separate installs?

Package managers like pnpm rely on a global content-addressable store to save disk space and speed up installations. When you run installations across separate projects or git worktrees on the same machine, the global store holds a single immutable copy of every package file. Official documentation details how pnpm symlinked node_modules structure uses hardlinks from this global store to construct local project structures.

When a new project requests a dependency, pnpm checks the global store. If the file already exists, it creates a hardlink rather than downloading or unpacking a fresh copy. This means package tarballs, extracted source files, and metadata are shared globally across all installs on your system. However, this sharing is strictly limited to immutable package contents. It does not extend to the dependency graph resolution itself, which is governed by each project’s individual lockfile.

Different lockfiles create distinct dependency graphs even when they pull from the same underlying global store. If worktree A and worktree B use slightly different dependency versions or separate workspace configurations, their resolved dependency structures remain entirely independent. Recording the fixture, observed result, and environment before calling any caching strategy tested is essential for verifying that no cross-contamination occurs between separate dependency trees. Before finalizing changes, developers often Audit Macos System Data Before Deleting to reclaim space safely without removing critical caches or disrupting active project files.

Why is one shared node_modules tree different from one shared store?

A common misunderstanding is treating a shared node_modules directory the same way as a shared content-addressable store. A shared store contains immutable files identified by their cryptographic content hashes. Because the files are read-only and immutable, multiple processes can safely link to them simultaneously without risk of data corruption.

In contrast, a node_modules directory generated by traditional package managers or misconfigured setups is mutable. It contains symlinks, lifecycle script execution remnants, and sometimes package-local state. Pointing multiple parallel git worktrees to a single shared node_modules folder invites race conditions. If two build agents attempt to run lifecycle scripts or write intermediate files into the same node_modules tree concurrently, one process will overwrite the changes of the other.

Furthermore, the layout of node_modules in pnpm is non-standard. It uses a hidden virtual directory structure (.pnpm) to construct a strict dependency graph where packages can only access declared dependencies. Sharing this mutable tree across concurrent tasks breaks the encapsulation guarantees of the package manager. Each worktree must maintain its own local node_modules structure, even while drawing its underlying file contents from the shared global store. Official guidelines on Git worktree documentation emphasize that worktrees share the same repository database but maintain separate working trees, making independent local dependency directories a natural requirement.

Which build directories need per-task isolation?

While package contents in the global store can be shared safely, compiler outputs, temporary build caches, and artifact directories must be strictly isolated per task. When running parallel builds across multiple git worktrees, shared build directories become a primary source of non-deterministic build failures.

Compilers and bundlers often write incremental build caches, TypeScript type-checking info, and transpiled JavaScript files directly into directories like dist, build, .tsbuildinfo, or .next. If two separate worktrees target the same output folder, task A might read an incremental build artifact generated by task B, leading to stale assets or missing type definitions.

Option When to use it Trade-off or failure mode Recommendation
Shared Global Store Across all local projects and worktrees Requires disk space management; corruption possible if store permissions are misconfigured Use for all read-only package storage
Isolated node_modules Per git worktree Consumes inode count and local filesystem metadata Mandatory for every independent worktree
Isolated Build Output Directories Parallel tasks and CI build agents Slightly increases total disk usage for temporary artifacts Mandatory to prevent cross-task overwrite bugs

To prevent these issues, configure your build tools to write to task-specific or process-isolated directories when executing concurrent tasks. Measured savings should identify filesystem and measurement method separately before claiming storage efficiency, as deduplication savings in the store do not translate directly to savings in mutable build output folders.

Additional considerations for multi-worktree concurrency

Managing multiple branches simultaneously requires clear operational discipline. Beyond separating your workspace build targets and keeping lockfiles synchronized, you must account for lock contention inside the global store itself. When multiple background install processes trigger simultaneously across different worktrees, they might attempt to write identical package payloads into the shared store at the exact same moment. Fortunately, pnpm employs atomic file operations and content-addressable hashing to make concurrent store writes safe under normal operating conditions. Nevertheless, if disk input-output bottlenecks occur, staggering your install triggers or running jobs sequentially through a task runner can significantly stabilize local development loops without sacrificing disk space efficiency.

Verification and execution checklist

Conclusion

Pnpm shares package contents between separate installs through a global content-addressable store, but each project maintains its own dependency graph and node_modules structure. By understanding what gets shared globally versus what must remain isolated, developers can prevent mysterious build failures and ensure reliable concurrent builds.

Continue Exploring

You Might Also Like

View all articles
Serverless Request Budget
2 min read

Serverless Request Budget

Reserve publication reads before background queue cleanup to prevent request budget starvation in a serverless publishing workflow.