# Turborepo Caching Strategies Local caching, remote caching, cache invalidation, and optimization techniques. ## Local Caching ### How It Works Turborepo caches task outputs based on inputs: 1. **Hash inputs**: Source files, dependencies, environment variables, config 2. **Run task**: If hash not in cache 3. **Save outputs**: Store in `.turbo/cache` 4. **Restore on match**: Instant completion on cache hit Default cache location: `./node_modules/.cache/turbo` ### Cache Configuration ```json // turbo.json { "pipeline": { "build": { "outputs": ["dist/**", ".next/**", "!.next/cache/**"], "cache": true // default }, "dev": { "cache": false // don't cache dev servers } } } ``` ### Outputs Configuration Specify what gets cached: ```json { "build": { "outputs": [ "dist/**", // All files in dist "build/**", // Build directory ".next/**", // Next.js output "!.next/cache/**", // Exclude Next.js cache "storybook-static/**", // Storybook build "*.tsbuildinfo" // TypeScript build info ] } } ``` **Best practices:** - Include all build artifacts - Exclude nested caches - Include type definitions - Include generated files ### Clear Local Cache ```bash # Remove cache directory rm -rf ./node_modules/.cache/turbo # Or use turbo command with --force turbo run build --force # Clear and rebuild turbo run clean && turbo run build ``` ## Remote Caching Share cache across team and CI/CD. ### Vercel Remote Cache (Recommended) **Setup:** ```bash # Login to Vercel turbo login # Link repository turbo link ``` **Use in CI:** ```yaml # .github/workflows/ci.yml env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} steps: - run: turbo run build test ``` Get tokens from Vercel dashboard: 1. Go to https://vercel.com/account/tokens 2. Create new token 3. Add as GitHub secrets ### Custom Remote Cache Configure custom remote cache server: ```json // .turbo/config.json { "teamid": "team_xxx", "apiurl": "https://cache.example.com", "token": "your-token" } ``` Or use environment variables: ```bash export TURBO_API="https://cache.example.com" export TURBO_TOKEN="your-token" export TURBO_TEAM="team_xxx" ``` ### Remote Cache Verification ```bash # Check cache status turbo run build --output-logs=hash-only # Output shows: # • web:build: cache hit, replaying logs [hash] # • api:build: cache miss, executing [hash] ``` ## Cache Signatures Cache invalidated when these change: ### 1. Source Files All tracked Git files in package: ``` packages/ui/ ├── src/ │ ├── button.tsx # Tracked │ └── input.tsx # Tracked ├── dist/ # Ignored (in .gitignore) └── node_modules/ # Ignored ``` ### 2. Package Dependencies Changes in package.json: ```json { "dependencies": { "react": "18.2.0" // Version change invalidates cache } } ``` ### 3. Environment Variables Configured in pipeline: ```json { "build": { "env": ["NODE_ENV", "API_URL"] // Changes invalidate cache } } ``` ### 4. Global Dependencies Files affecting all packages: ```json { "globalDependencies": [ "**/.env.*local", "tsconfig.json", ".eslintrc.js" ] } ``` ### 5. Task Configuration Changes to turbo.json pipeline: ```json { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] // Config changes invalidate cache } } ``` ## Input Control ### Override Input Detection Explicitly define what affects cache: ```json { "build": { "inputs": [ "src/**/*.ts", // Include TS files "src/**/*.tsx", // Include TSX files "!src/**/*.test.ts", // Exclude tests "!src/**/*.stories.tsx", // Exclude stories "package.json", // Include package.json "tsconfig.json" // Include config ] } } ``` Use cases: - Exclude test files from build cache - Exclude documentation from production builds - Include only source files, not generated files ### Global vs Package Inputs **Global inputs** (affect all packages): ```json { "globalDependencies": [".env", "tsconfig.json"] } ``` **Package inputs** (affect specific tasks): ```json { "pipeline": { "build": { "inputs": ["src/**"] } } } ``` ## Environment Variables ### Cached Environment Variables Include in cache signature: ```json { "pipeline": { "build": { "env": [ "NODE_ENV", // Must match for cache hit "NEXT_PUBLIC_API_URL", "DATABASE_URL" ] } } } ``` Cache invalidated when values change. ### Pass-Through Environment Variables Don't affect cache: ```json { "pipeline": { "build": { "passThroughEnv": [ "DEBUG", // Different values use same cache "LOG_LEVEL", "VERBOSE" ] } } } ``` Use for: Debug flags, log levels, non-production settings ### Global Environment Variables Available to all tasks: ```json { "globalEnv": [ "NODE_ENV", "CI", "VERCEL" ] } ``` ## Cache Optimization Strategies ### 1. Granular Outputs Define precise outputs to minimize cache size: ```json // ❌ Bad - caches too much { "build": { "outputs": ["**"] } } // ✅ Good - specific outputs { "build": { "outputs": ["dist/**", "!dist/**/*.map"] } } ``` ### 2. Exclude Unnecessary Files ```json { "build": { "outputs": [ ".next/**", "!.next/cache/**", // Exclude Next.js cache "!.next/server/**/*.js.map", // Exclude source maps "!.next/static/**/*.map" ] } } ``` ### 3. Separate Cacheable Tasks ```json { "pipeline": { "build": { "dependsOn": ["^build"], "cache": true }, "test": { "dependsOn": ["build"], "cache": true // Separate from build }, "dev": { "cache": false // Never cache } } } ``` ### 4. Use Input Filters Only track relevant files: ```json { "build": { "inputs": [ "src/**/*.{ts,tsx}", "!src/**/*.{test,spec}.{ts,tsx}", "public/**", "package.json" ] } } ``` ## Cache Analysis ### Inspect Cache Hits/Misses ```bash # Dry run with JSON output turbo run build --dry-run=json | jq '.tasks[] | {package: .package, task: .task, cache: .cache}' ``` ### View Task Graph ```bash # Generate task graph turbo run build --graph # Output: graph.html (open in browser) ``` ### Cache Statistics ```bash # Run with summary turbo run build --summarize # Output: .turbo/runs/[hash].json ``` ## CI/CD Cache Configuration ### GitHub Actions ```yaml name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 18 - name: Install dependencies run: npm install - name: Build and test run: turbo run build test lint env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} # Optional: Cache node_modules - uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} ``` ### GitLab CI ```yaml image: node:18 cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ - .turbo/ build: stage: build script: - npm install - turbo run build test variables: TURBO_TOKEN: $TURBO_TOKEN TURBO_TEAM: $TURBO_TEAM ``` ## Troubleshooting ### Cache Not Working **Check outputs are defined:** ```bash turbo run build --dry-run=json | jq '.tasks[] | {task: .task, outputs: .outputs}' ``` **Verify cache location:** ```bash ls -la ./node_modules/.cache/turbo ``` **Check environment variables:** ```bash echo $TURBO_TOKEN echo $TURBO_TEAM ``` ### Cache Too Large **Analyze cache size:** ```bash du -sh ./node_modules/.cache/turbo ``` **Reduce outputs:** ```json { "build": { "outputs": [ "dist/**", "!dist/**/*.map", // Exclude source maps "!dist/**/*.test.js" // Exclude test files ] } } ``` **Clear old cache:** ```bash # Turborepo doesn't auto-clean, manually remove: rm -rf ./node_modules/.cache/turbo ``` ### Remote Cache Connection Issues **Test connection:** ```bash curl -I https://cache.example.com ``` **Verify token:** ```bash turbo link # Should show: "Remote caching enabled" ``` **Check logs:** ```bash turbo run build --output-logs=full ``` ## Best Practices 1. **Define precise outputs** - Only cache necessary files 2. **Exclude nested caches** - Don't cache caches (.next/cache) 3. **Use remote caching** - Share cache across team and CI 4. **Track relevant inputs** - Use `inputs` to filter files 5. **Separate env vars** - Use `passThroughEnv` for debug flags 6. **Cache test results** - Include coverage in outputs 7. **Don't cache dev servers** - Set `cache: false` for dev tasks 8. **Use global dependencies** - Share config across packages 9. **Monitor cache performance** - Use `--summarize` to analyze 10. **Clear cache periodically** - Remove stale cache manually ## Cache Performance Tips **For CI/CD:** - Enable remote caching - Run only changed packages: `--filter='...[origin/main]'` - Use `--continue` to see all errors - Cache node_modules separately **For Local Development:** - Keep local cache enabled - Don't force rebuild unless needed - Use filters to build only what changed - Clear cache if issues arise **For Large Monorepos:** - Use granular outputs - Implement input filters - Monitor cache size regularly - Consider cache size limits on remote cache