pnpm Complete Guide | Fast Disk Space Efficient Package Manager
이 글의 핵심
pnpm is a fast, disk space efficient package manager for Node.js. It uses a content-addressable store to save disk space and is up to 3x faster than npm.
Introduction
pnpm (performant npm) is a fast, disk space efficient package manager. It solves npm’s performance and disk space issues by using a global content-addressable store.
The Problem with npm
npm/yarn:
# Each project duplicates node_modules
project1/node_modules/lodash # 2MB
project2/node_modules/lodash # 2MB
project3/node_modules/lodash # 2MB
# Total: 6MB for 3 projects
pnpm:
# One copy in global store, hard links in projects
~/.pnpm-store/lodash # 2MB (once)
project1/node_modules/lodash # hard link (0 bytes)
project2/node_modules/lodash # hard link (0 bytes)
project3/node_modules/lodash # hard link (0 bytes)
# Total: 2MB for 3 projects
1. Installation
# Via npm
npm install -g pnpm
# Via standalone script (recommended)
curl -fsSL https://get.pnpm.io/install.sh | sh -
# Via Homebrew (macOS)
brew install pnpm
# Via Volta
volta install pnpm
Verify installation:
pnpm --version
2. Basic Commands
Install Dependencies
# Install all dependencies
pnpm install
# Add a dependency
pnpm add express
# Add dev dependency
pnpm add -D typescript
# Add global package
pnpm add -g pm2
Remove Dependencies
# Remove package
pnpm remove express
# Remove dev dependency
pnpm remove -D typescript
Update Dependencies
# Update all dependencies
pnpm update
# Update specific package
pnpm update express
# Update to latest (ignore semver)
pnpm update express --latest
3. Speed Comparison
| Command | npm | yarn | pnpm |
|---|---|---|---|
| Install | 51s | 37s | 16s |
| Install (cache) | 33s | 11s | 1.5s |
| Install (lockfile) | 28s | 11s | 5s |
pnpm is 2-3x faster!
4. Disk Space Efficiency
# Check disk usage
pnpm store status
# Example output:
# Content-addressable store is at: ~/.pnpm-store
# Total size: 2.5 GB
# Projects using store: 47
# Equivalent npm size: ~15 GB (saved 12.5 GB!)
5. Workspaces (Monorepo)
Setup
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
Project structure:
monorepo/
├── pnpm-workspace.yaml
├── package.json
├── packages/
│ ├── ui/
│ │ └── package.json
│ └── utils/
│ └── package.json
└── apps/
├── web/
│ └── package.json
└── api/
└── package.json
Install Dependencies
# Install all workspace dependencies
pnpm install
# Install for specific package
pnpm --filter web add react
# Install for all packages
pnpm -r add lodash
Run Scripts
# Run script in specific package
pnpm --filter web dev
# Run in all packages
pnpm -r build
# Run in parallel
pnpm -r --parallel test
6. Filtering
# Install dependencies for 'web' package
pnpm --filter web install
# Run build for all packages in 'packages' folder
pnpm --filter './packages/*' build
# Run dev for 'web' and its dependencies
pnpm --filter web... dev
# Run build for packages that depend on 'utils'
pnpm --filter ...utils build
7. Scripts
// package.json
{
"scripts": {
"dev": "pnpm --filter web dev",
"build": "pnpm -r build",
"test": "pnpm -r --parallel test",
"clean": "pnpm -r exec rm -rf dist"
}
}
8. Configuration
# .npmrc
# Store location
store-dir=~/.pnpm-store
# Symlink node_modules
node-linker=hoisted
# Strict peer dependencies
strict-peer-dependencies=true
# Auto install peer dependencies
auto-install-peers=true
# Shamefully hoist (for compatibility)
shamefully-hoist=false
# Lockfile location
lockfile=true
9. Migration from npm/yarn
Step 1: Remove old files
rm -rf node_modules package-lock.json yarn.lock
Step 2: Install with pnpm
pnpm install
Step 3: Update CI/CD
# GitHub Actions
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install
- run: pnpm build
10. Real-World Example: Next.js Monorepo
Structure:
nextjs-monorepo/
├── pnpm-workspace.yaml
├── package.json
├── apps/
│ ├── web/
│ │ ├── package.json
│ │ └── next.config.js
│ └── admin/
│ ├── package.json
│ └── next.config.js
└── packages/
├── ui/
│ ├── package.json
│ └── src/
└── config/
└── package.json
pnpm-workspace.yaml:
packages:
- 'apps/*'
- 'packages/*'
Root package.json:
{
"name": "nextjs-monorepo",
"private": true,
"scripts": {
"dev": "pnpm --filter web dev",
"dev:admin": "pnpm --filter admin dev",
"build": "pnpm -r build",
"lint": "pnpm -r lint",
"test": "pnpm -r test"
},
"devDependencies": {
"typescript": "^5.0.0",
"eslint": "^8.0.0"
}
}
apps/web/package.json:
{
"name": "web",
"version": "1.0.0",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "^14.0.0",
"react": "^18.0.0",
"ui": "workspace:*"
}
}
packages/ui/package.json:
{
"name": "ui",
"version": "1.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts",
"dependencies": {
"react": "^18.0.0"
}
}
11. Advanced Features
Patching Packages
# Create patch
pnpm patch [email protected]
# Edit files in temporary directory
# Save and generate patch
pnpm patch-commit /tmp/patch-123
# Patch is saved to patches/[email protected]
Overrides
// package.json
{
"pnpm": {
"overrides": {
"axios": "1.0.0",
"react@<18.0.0": "18.0.0"
}
}
}
Catalog
# pnpm-workspace.yaml
catalog:
react: ^18.0.0
typescript: ^5.0.0
// package.json
{
"dependencies": {
"react": "catalog:"
}
}
12. Docker Integration
# Dockerfile
FROM node:18-alpine
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# Copy workspace files
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY package.json ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source
COPY . .
# Build
RUN pnpm build
CMD ["pnpm", "start"]
With layer caching:
FROM node:18-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN pnpm install --frozen-lockfile --prod
FROM node:18-alpine AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:18-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
CMD ["node", "dist/index.js"]
13. Troubleshooting
Problem: Peer dependency warnings
# Auto install peer dependencies
pnpm add --save-peer react-dom
# Or configure .npmrc
echo "auto-install-peers=true" >> .npmrc
Problem: Module not found
# Use shamefully-hoist for compatibility
echo "shamefully-hoist=true" >> .npmrc
pnpm install
Problem: Slow installation
# Clear cache
pnpm store prune
# Use faster mirror (China)
pnpm config set registry https://registry.npmmirror.com
14. Best Practices
1. Use workspace protocol
{
"dependencies": {
"ui": "workspace:*" // Any version from workspace
}
}
2. Pin pnpm version
// package.json
{
"packageManager": "[email protected]"
}
3. Use .npmrc for team settings
# .npmrc
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=false
4. Commit pnpm-lock.yaml
git add pnpm-lock.yaml
git commit -m "chore: update dependencies"
15. Performance Tips
1. Use frozen lockfile in CI
pnpm install --frozen-lockfile
2. Prune store regularly
pnpm store prune
3. Use offline mode
pnpm install --offline
Summary
pnpm revolutionizes Node.js package management:
- 3x faster than npm
- Saves disk space with content-addressable store
- Monorepo support with workspaces
- 100% compatible with npm packages
- Strict by default prevents phantom dependencies
Key Takeaways:
- Uses hard links to save disk space
- 2-3x faster than npm/yarn
- Built-in monorepo support
- Compatible with all npm packages
- Strict dependency resolution
Next Steps:
- Try npm
- Learn Yarn
- Build Monorepos
Resources: