PNPM Optimization Strategies
Performance optimizations for PNPM dependency installation in GitHub Actions workflows.
This document outlines the PNPM optimization strategies implemented across our GitHub Actions workflows to dramatically improve dependency installation performance and CI execution times.
Key Optimization Strategies
1. Updated PNPM Action Version
We've upgraded from pnpm/action-setup@v2 to pnpm/action-setup@v4 for improved performance and latest features.
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false2. Optimized PNPM Store Caching
Our composite action implements manual PNPM store caching for better control and performance:
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm store cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ component }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-${{ component }}-
${{ runner.os }}-pnpm-store-3. Optimized Installation Flags
Dependencies are installed with performance-focused flags:
pnpm install --frozen-lockfile --prefer-offline --no-auditFlag explanations:
--frozen-lockfile: Ensures exact reproducibility and faster installs--prefer-offline: Uses cached packages when available, avoiding unnecessary network calls--no-audit: Skips security audits during CI for faster execution
4. Composite Action for DRY Principle
We've created a reusable composite action at .github/actions/pnpm-setup/action.yml that eliminates duplication across workflows:
- name: Setup PNPM with optimized caching
uses: ./.github/actions/pnpm-setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
cache-strategy: 'manual'
component: ${{ matrix.component }}Cache Strategy Options:
manual: Uses optimized PNPM store caching (recommended for most cases)builtin: Usesactions/setup-node@v4built-in PNPM caching (simpler but less control)
Performance Impact
Expected Improvements
Based on comprehensive research and implementations by other teams:
- Speed Improvements: Teams report reducing deployment times from 26 minutes to 1 minute (25x improvement)
- Network Optimization:
--prefer-offlineflag alone provides 15-20% speed improvements - Cache Efficiency: Component-specific cache keys improve cache hit rates
Monitoring Performance
The workflows include basic execution time reporting. For detailed performance monitoring:
- Check workflow run duration in GitHub Actions
- Monitor cache hit rates in workflow logs
- Compare before/after implementation metrics
Configuration Options
Component-Specific Caching
The composite action supports component-specific cache keys for better cache isolation:
component: ${{ matrix.component }} # Creates component-specific cache keysVersion Consistency
Always pin PNPM versions to avoid lockfile compatibility issues:
env:
PNPM_VERSION: '9' # Matches packageManager field in package.jsonImplementation Details
Workflows Updated
The following workflows have been optimized:
- Main CI (
.github/workflows/ci.yml): All jobs (typecheck, build, lint, format) - Storybook Tests (
.github/workflows/storybook-tests.yml): UI component testing - Knip (
.github/workflows/knip.yml): Unused code detection
Cache Key Strategy
Cache keys use precise patterns for optimal cache invalidation:
key: ${{ runner.os }}-pnpm-store-${{ component }}-${{ hashFiles('**/pnpm-lock.yaml') }}This ensures:
- New caches are created only when dependencies actually change
- Component isolation prevents cache conflicts
- Fallback patterns allow incremental improvements
Best Practices
1. Version Alignment
Keep Node.js and PNPM versions aligned between local and CI environments to prevent lockfile compatibility issues.
2. Cache Directory Precision
Always use pnpm store path --silent to get the correct store directory rather than hardcoding paths.
3. Fallback Strategy
Include restore-keys to allow partial cache matches, enabling incremental improvements even with lockfile changes.
4. Monorepo Optimization
For workspaces, the composite action automatically handles dependency caching per component for optimal performance.
Troubleshooting
Common Issues
Frozen Lockfile Errors: Ensure PNPM versions match between local and CI environments. Check the packageManager field in your root package.json.
Cache Misses: Verify that pnpm-lock.yaml is committed and unchanged. Check cache key patterns in workflow logs.
Network Issues: For self-hosted runners, consider local caching solutions as GitHub's remote cache can be slower over public internet connections.
Debugging Cache Performance
- Check cache hit/miss status in workflow logs
- Verify store path detection:
pnpm store path --silent - Monitor actual dependency installation times
- Compare cache key patterns between workflows
Future Optimizations
Potential additional improvements:
- Conditional Installation: Skip reinstallation when cache is hit perfectly
- Workspace-Specific Optimization: Further optimize for monorepo workspace dependencies
- Build Artifact Caching: Enhanced caching for build outputs and TypeScript incremental builds
- Performance Monitoring: Automated performance regression detection