pnpm Workspaces: Fast, Deterministic Monorepo Setup
pnpm workspaces let you share dependencies across a monorepo, cutting install time and disk usage. This post walks through setting up a minimal workspace, explains hoisting, and shows how to diagnose common pitfalls.
06 Aug 2025, 10:43 UTC

Problem: Fragmented Dependencies in a Monorepo
When a repo grows into multiple packages, each with its own node_modules, you end up with duplicated binaries, inconsistent versions, and long install times. The classic approach with npm or Yarn creates a flat node_modules per package, but that still duplicates files across the repo. The question is: how can we keep a single source of truth for dependencies while still allowing each package to import them locally?
Solution: pnpm Workspaces
pnpm’s workspace feature automatically links local packages defined in a pnpm-workspace.yaml file. It also uses a global, content‑addressable store that deduplicates binaries across all workspaces, and a single lockfile pnpm-lock.yaml that guarantees deterministic installs.
Key Benefits
- Shared dependency versions across the repo.
- Installation time and disk usage drop dramatically.
- Deterministic lockfile that CI can trust.
Setting Up a Minimal Workspace
Below is a quick recipe you can copy‑paste into a fresh folder.
mkdir my-monorepo
cd my-monorepo
# Root package.json (optional, but useful for scripts)
echo '{ "private": true, "scripts": { "build": "pnpm --filter \"./packages/*\" run build" } }' > package.json
# Workspace config
echo '
packages:
- packages/*
' > pnpm-workspace.yaml
# Create two packages
mkdir -p packages/lib
mkdir -p packages/app
# package.json for lib
cat > packages/lib/package.json < packages/app/package.json <
Running pnpm install from the root will:
- Resolve
lodashonce in the global store. - Create a symlink from
packages/app/node_modules/@myorg/libtopackages/lib/node_modules. - Populate
pnpm-lock.yamlwith a single entry forlodash@4.17.21(example version).
How the Store Works
pnpm stores each unique tarball in ~/.pnpm-store. When a package needs a dependency, it creates a hard link to that file instead of copying it. This means you only pay for a dependency once, no matter how many packages need it.
Fine‑Grained Control: nohoist and packages
By default pnpm hoists dependencies to the workspace root node_modules. That keeps the root tidy but can bloat it. If a tool expects a flat layout, you can prevent hoisting for specific packages:
# pnpm-workspace.yaml
packages:
- packages/*
nohoist:
- '@myorg/lib/**'
Now lodash will stay inside packages/lib/node_modules, and packages/app will resolve it from @myorg/lib instead of the root.
Diagnosing Problems
Use pnpm why lodash from any package to see the dependency chain. If you see two separate entries for the same version, something went wrong with the store or the workspace config.
| Command | What to Check |
|---|---|
| pnpm install | Look for “linking” logs indicating symlinks are created. |
| pnpm why lodash | Ensure the path points to packages/lib/node_modules/lodash. |
| du -sh ~/.pnpm-store | grep lodash | Only one tarball should exist. |
Trade‑Offs and Limitations
- Missing
pnpm-workspace.yaml: pnpm treats the repo as a single package, losing workspace benefits. - Hoisting bloat: The root
node_modulescan become large if many dependencies are hoisted. - Tooling assumptions: Some scripts written for npm’s flat layout may break; adjust paths or use
--filterto target packages. - Lockfile incompatibility:
pnpm-lock.yamlcannot be shared with npm or Yarn; switching tools requires a fresh lockfile.
Practical Checklist Before You Commit
- Confirm
pnpm-workspace.yamlis present at the repo root. - Run
pnpm installand verify symlinks innode_modulesfolders. - Check
pnpm-lock.yamlfor deterministic entries. - Test a build script from the root:
pnpm run build. - Measure install time and disk usage vs. a non‑workspace setup.
Conclusion
pnpm’s workspace feature gives you a clean, fast, and deterministic monorepo workflow. By linking local packages, sharing a global store, and using a single lockfile, you eliminate duplication and ensure that every environment – from your laptop to CI – sees the same dependency graph. Just remember to keep the workspace config in sync, be mindful of hoisting, and adjust any legacy scripts that expect npm’s node_modules layout.
Ready to give it a try? Create a pnpm-workspace.yaml, add a couple of packages, and run pnpm install. The rest is just a matter of making sure your tooling plays nicely with the workspace structure.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.