Skip to content
Harjot Singh Rana

Full-Stack & AI Product Engineer

All writing

The Practical Vercel Turborepo Cheatsheet

Task graphs, remote caching, and the pipeline config that actually keeps a monorepo fast.

Turborepo is a build system optimized for JavaScript and TypeScript monorepos, written in Rust. It handles task orchestration, caching, and dependency management across multiple packages in a single repository.


Core Concepts

Monorepo Structure

my-monorepo/
├── apps/
│   ├── web/
│   ├── mobile/
│   └── admin/
├── packages/
│   ├── ui-library/
│   ├── utils/
│   └── types/
├── turbo.json (configuration)
├── package.json (root workspace)
└── pnpm-workspace.yaml (or package.json for npm/yarn workspaces)

Key Concepts

  • Package Graph: Relationship between all packages in the monorepo
  • Task Graph: Dependency relationships between tasks
  • Caching: Turborepo remembers task outputs and reuses them when inputs haven't changed
  • Remote Caching: Share cache artifacts across team members and CI/CD systems

Installation & Setup

Initialize a New Turborepo

npx create-turbo@latest

Add Turborepo to Existing Project

npm install turbo --save-dev

# Initialize configuration
turbo init

turbo.json Configuration

Basic Structure

{
  "extends": ["//"],
  "globalDependencies": ["*.env", "package.json"],
  "globalEnv": ["NODE_ENV"],
  "globalPassThroughEnv": ["HOME", "PATH"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "cache": true,
      "env": ["NODE_ENV", "API_*"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    },
    "lint": {
      "outputs": []
    }
  }
}

Task Definition Options

OptionDescriptionExample
dependsOnTasks that must complete first["^build"], ["build", "lint"]
outputsFiles to cache["dist/**", ".next/**"]
cacheEnable/disable cachingtrue or false
envEnvironment variables that affect hashing["NODE_ENV", "API_*"]
passThroughEnvEnvironment variables available but not cached["HOME"]
inputsFiles to consider for cache invalidation["src/**", "package.json"]
persistentMark as long-running (dev servers)true
interactiveAccept stdin inputtrue
interruptibleAllow restart by turbo watchtrue
outputLogsLog verbosity"full", "hash-only", "new-only", "errors-only", "none"

Dependency Prefix Symbols

SymbolMeaningExample
^Depends on same task in dependencies"^build" (wait for deps to build)
No prefixSame-package dependency"build" (in test task = test after build in same pkg)
package#taskSpecific package task"utils#build" (utils package build task)

Common turbo.json Patterns

Pattern 1: Build with Dependencies

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    }
  }
}

Builds dependencies first, then this package.

Pattern 2: Test Before Build

{
  "pipeline": {
    "build": {
      "dependsOn": ["test", "^build"],
      "outputs": ["dist/**"]
    }
  }
}

Runs local tests, then dependency builds, then this package build.

Pattern 3: Cache-Only Tasks

{
  "pipeline": {
    "test": {
      "cache": true,
      "outputs": ["coverage/**"]
    }
  }
}

Tests are cached; only re-run on source code changes.

Pattern 4: Never Cache

{
  "pipeline": {
    "deploy": {
      "cache": false
    }
  }
}

Useful for deployment and external API calls.

Pattern 5: Long-Running Tasks (Dev Server)

{
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    }
  }
}

Dev servers run continuously and accept input.


CLI Commands

Run Tasks

# Run a single task in all packages
turbo run build

# Run multiple tasks
turbo run build lint test

# Run task in specific package
turbo run build --filter=ui

# Run without dependencies (immediate task only)
turbo run test --only

# Run only affected packages (git-based)
turbo run build --affected

# Run with all environment variables available
turbo run build --env-mode=loose

# Run in parallel (ignoring dependency graph)
turbo run dev --parallel

# Force re-run (ignore cache)
turbo run build --force

# Run with verbose output
turbo run build -v
turbo run build -vv   # more verbose
turbo run build -vvv  # very verbose

Filtering

# By package name
turbo run build --filter=web

# By directory pattern
turbo run build --filter=./apps/*

# By git changes
turbo run build --filter=[HEAD^1]

# Select dependents (packages depending on target)
turbo run build --filter=...ui

# Select dependencies (packages target depends on)
turbo run build --filter=ui...

# Exclude packages
turbo run build --filter=!admin

# Combine filters (union)
turbo run build --filter=web --filter=mobile

# Specific task in package
turbo run web#build

Cache Management

# Disable caching
turbo run build --no-cache

# Use only local cache
turbo run build --cache=local:rw

# Use only remote cache
turbo run build --cache=remote:rw

# Read-only cache
turbo run build --cache=local:r

# Disable cache completely
turbo run build --cache=local:,remote:

Other Useful Commands

# Dry run (show what would execute, don't run)
turbo run build --dry-run

# Dry run as JSON
turbo run build --dry=json

# Show task execution order without running
turbo run build --graph=mermaid

# Generate performance trace (Chrome Tracing format)
turbo run build --profile -vv

# Generate run summary (metadata about execution)
turbo run build --summarize

# Continue on error
turbo run build --continue=always

# Limit concurrency
turbo run build --concurrency=4
turbo run build --concurrency=50%

# Watch mode (watch for changes and re-run)
turbo watch run build

Remote Caching (Vercel)

# Login to Vercel
turbo login

# Link to remote cache
turbo link

# Link specific project in package
cd apps/web && turbo link

# Logout
turbo logout

# Verify remote cache is working
turbo run build --summarize

Utility Commands

# List all packages in workspace
turbo ls

# Check workspace info
turbo info

# Check package boundaries/dependencies
turbo boundaries

# Generate new package/code
turbo gen

# Prune dependency graph (for Docker/CI)
turbo prune --scope=web

# Check for missing environment variables
turbo scan

# Get Turborepo version
turbo --version

Environment Variables

Global Environment Variables (turbo.json)

{
  "globalEnv": ["NODE_ENV", "API_KEY"],
  "globalPassThroughEnv": ["HOME", "PATH"]
}
  • globalEnv: Changes invalidate ALL task caches
  • globalPassThroughEnv: Available to tasks but don't affect caching

Task-Specific Environment Variables

{
  "pipeline": {
    "build": {
      "env": ["NODE_ENV", "NEXT_PUBLIC_*"],
      "passThroughEnv": ["HOME"]
    }
  }
}

Environment Variable Patterns

{
  "env": [
    "*",                 // All variables
    "API_*",            // Prefix match
    "!API_SECRET",      // Negation (exclude)
    "NODE_ENV"          // Exact match
  ]
}

Strict vs Loose Mode

# Strict mode (default) - only listed vars available
turbo run build --env-mode=strict

# Loose mode - all vars available (less safe for caching)
turbo run build --env-mode=loose

Package Configurations

Use for task customization per-package:

// packages/ui/turbo.json
{
  "extends": ["//"],
  "pipeline": {
    "build": {
      "outputs": ["dist/**"],
      "env": ["THEME_*"]
    }
  }
}

Common Patterns & Best Practices

1. Workspace Dependencies

// apps/web/package.json
{
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/utils": "workspace:*"
  }
}

2. Shared Configuration

// Root turbo.json
{
  "globalDependencies": [
    "package.json",
    "pnpm-lock.yaml",
    ".env.example"
  ]
}

3. CI/CD Optimization

# In GitHub Actions
turbo run build --affected --cache=remote:rw

# In other CI systems
turbo run build --force --cache=local:rw

4. Docker/Monorepo Pruning

# Prune only dependencies needed for `web` app
turbo prune --scope=web --docker

# Then in Dockerfile
COPY --from=base /repo/out/full /repo
COPY --from=base /repo/out/json . 
COPY --from=base /repo/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN pnpm install --frozen-lockfile

5. Task Skipping (turbo-ignore)

# In CI environment variable
TURBO_TELEMETRY_DISABLED=1

# Skip build if only docs changed
turbo-ignore docs

Caching Strategy

What Gets Cached

  • Outputs: Files specified in outputs key
  • Logs: Task logs (always cached if caching enabled)
  • Metadata: Task execution metadata

Cache Invalidation

Cache is invalidated when:

  • Source files in inputs change
  • globalDependencies files change
  • Environment variables in env change
  • turbo.json changes
  • package.json changes (in that package)
  • Lockfile changes

Cache Locations

# Local cache (default)
.turbo/cache/

# Custom cache directory
turbo run build --cache-dir=.turbo-cache

# Or in turbo.json
{
  "cacheDir": ".turbo-cache"
}

Debugging & Troubleshooting

Check Task Configuration

# See what tasks will execute
turbo run build --dry-run

# Get detailed execution info
turbo run build --dry=json | jq

# Verbose output
turbo run build -vvv

Analyze Performance

# Generate Chrome trace
turbo run build --profile -vv

# Check hash and dependencies
turbo run build --dry-run | grep -A5 "web#build"

# See execution summary
turbo run build --summarize
# View in `.turbo/runs/` directory

Common Issues

IssueSolution
Cache not workingCheck outputs glob pattern; verify env vars in env key
Task not runningCheck dependsOn; use --dry-run to verify task graph
Environment vars not availableAdd to env in turbo.json for hashing, passThroughEnv for availability
Performance degradationCheck if inputs are too broad; optimize glob patterns
Package not foundVerify package.json name; use turbo ls to list packages

Advanced Tips

1. Framework Inference

Turborepo automatically includes framework-specific env vars:

  • Next.js: NEXT_PUBLIC_* (automatic)
  • Nuxt: NUXT_* (automatic)
  • Vue: VUE_APP_* (automatic)

2. Multiple Package Managers

{
  "packageManager": "pnpm@8.0.0"
}

3. Workspace Configuration

// pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools/*'
// package.json (npm/yarn)
{
  "workspaces": ["apps/*", "packages/*"]
}

4. Remote Cache with Custom Endpoint

{
  "remoteCache": {
    "apiUrl": "https://custom-cache.com",
    "signature": true,
    "timeout": 60,
    "uploadTimeout": 120
  }
}

5. Concurrency Control

# Serial execution (1 at a time)
turbo run build --concurrency=1

# Use 50% of CPU cores
turbo run build --concurrency=50%

# Or in turbo.json
{
  "concurrency": "50%"
}

Real-World Example

{
  "extends": ["//"],
  "globalDependencies": ["*.env", "package.json"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "cache": true,
      "env": ["NODE_ENV", "API_URL"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "cache": true,
      "outputs": []
    },
    "type-check": {
      "cache": true,
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    },
    "deploy": {
      "cache": false,
      "dependsOn": ["build", "test", "lint"]
    }
  }
}

Usage:

# Full CI pipeline
turbo run lint type-check test build

# Development
turbo run dev --parallel

# Deploy only changed packages
turbo run build --affected && turbo run deploy

# Rebuild everything
turbo run build --force

Resources


Quick Command Reference

# Development
turbo run dev --parallel

# Build
turbo run build

# Lint
turbo run lint

# Test
turbo run test

# All checks before commit
turbo run lint type-check test

# Force rebuild everything
turbo run build --force

# Only affected packages
turbo run build --affected

# Specific package
turbo run build --filter=web

# Watch mode
turbo watch run build

# See what would run
turbo run build --dry-run

# Performance analysis
turbo run build --profile -vv

# Remote cache
turbo login
turbo link
turbo run build

Last Updated: December 2025 | Based on Turborepo latest documentation

More in writing, or back home.