Hi everyone, yesterday I spent time migrating from Yarn to pnpm, hoping for potential memory savings from the content-addressable store (global store) and faster package installation. Everything was smooth — pnpm consistently outperformed Yarn in both cached and uncached cases.
But then I encountered something strange on macOS: it seemed like the Content-Addressable Store was broken.
I spent many hours debugging on macOS using the Storage UI, CLI (du -sh node_modules), etc., but nothing worked. As you can see in the picture, the size of node_modules is still the same, right?
In my head, I was still thinking: “Hmm… if our repositories can share packages from the global store, why is node_modules still so large?” Then I switched to Ubuntu, and voilà — it worked as expected. In the Storage UI, the size difference was clear: only ~6 MB per repository.
But wait a minute — after using the CLI command du -sh node_modules, the result was still the same on macOS… Hmm, maybe I misunderstood how the content-addressable store works.
I went to the pnpm GitHub issues and found some related discussions:
• https://github.com/pnpm/pnpm/issues/2761
• https://github.com/pnpm/pnpm/issues/9935
And then I realized: oh, I was really wrong about the definition of a content-addressable global store. So I spent more time reading about hardlinks and symlinks, and voilà — I found the root cause.
In my case, let’s say you have lodash duplicated in two repositories. pnpm does two “magic” things:
- Hardlink: It creates a hardlink version of lodash (only for files, not folders).
- Why hardlink? Because a hardlink allows you to create a clone of a file (not a folder) without increasing your storage usage. It only increases the link count (similar to a linked list) when you add or clone another version.
- Symlink: It creates a symlink (shortcut).
- Why symlink? Because Node.js module resolution (see official docs) relies on folder paths, not files, when you use require (CJS) or import (ESM).
Then I tested again on Ubuntu by checking the hardlink count with the CLI, and I saw 3 hardlink versions (one from pnpm-test, one from pnpm-test-1, and the original one from the global store) with same 828284 Inode number — Fanstatic!
ls -lai ./node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/package.json
But back on macOS, it only showed 1 and with a different inode. That means it’s not working as expected — this version did not clone from the global store.
Top comments (0)