Learn-skills.dev turborepo
Turborepo for monorepo management — task orchestration, caching, and workspace configuration. Use when user mentions "turborepo", "turbo", "monorepo", "workspace", "turbo.json", "pnpm workspaces", "npm workspaces", "shared packages", "monorepo build", or managing multiple packages in one repository.
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/1mangesh1/dev-skills-collection/turborepo" ~/.claude/skills/neversight-learn-skills-dev-turborepo-d496e0 && rm -rf "$T"
data/skills-md/1mangesh1/dev-skills-collection/turborepo/SKILL.mdTurborepo
Build system for JavaScript/TypeScript monorepos. Handles task orchestration, caching, and dependency-aware execution across workspaces.
Setup
# New monorepo npx create-turbo@latest my-monorepo # Add to existing repo pnpm add turbo --save-dev --workspace-root # or: npm install turbo --save-dev
Add
.turbo to .gitignore. Add root scripts:
{ "scripts": { "build": "turbo run build", "dev": "turbo run dev", "test": "turbo run test", "lint": "turbo run lint" } }
turbo.json Configuration
{ "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "build/**"], "env": ["NODE_ENV"] }, "test": { "dependsOn": ["build"], "outputs": [], "env": ["CI"] }, "lint": { "outputs": [] }, "dev": { "cache": false, "persistent": true }, "typecheck": { "dependsOn": ["^build"], "outputs": [] } } }
-- tasks that must complete first.dependsOn
prefix = topological (upstream workspaces).^
-- files produced by the task, used for caching.outputs
-- setcache
to disable caching.false
-- marks long-running tasks (dev servers).persistent
-- environment variables that affect the task hash.env
Turborepo v2 uses
"tasks" instead of the v1 "pipeline" key.
Workspace Structure
my-monorepo/ turbo.json package.json pnpm-workspace.yaml apps/ web/package.json api/package.json packages/ ui/package.json shared-types/package.json eslint-config/package.json tsconfig/package.json
Task Orchestration
turbo run build # all workspaces turbo run lint build test # multiple tasks turbo run build --concurrency=4 # limit parallelism turbo run build --concurrency=50% # percentage of CPU cores
Turbo analyzes the dependency graph and parallelizes where possible.
Task Dependencies
"^build" means run build in all upstream workspace dependencies first. If apps/web depends on packages/ui, then packages/ui#build runs before apps/web#build.
"lint" (no caret) means run lint in the same workspace first:
{ "test": { "dependsOn": ["lint", "build"] } }
Per-workspace overrides -- add a
turbo.json in the workspace directory:
{ "extends": ["//"], "tasks": { "build": { "outputs": [".next/**"] } } }
Caching
Turbo hashes inputs (source files, deps, env vars, config) and caches task outputs. On a cache hit, outputs are restored and logs replayed without re-execution.
turbo run build --no-cache # skip reading cache turbo run build --force # ignore cache, re-execute all
Remote caching
npx turbo login npx turbo link
Connects to Vercel for shared remote caching across team members and CI.
Self-hosted endpoint:
turbo run build --api="https://cache.example.com" --token="<token>"
Environment Variables
{ "tasks": { "build": { "env": ["API_URL", "DATABASE_URL"], "passThroughEnv": ["AWS_SECRET_ACCESS_KEY"] } }, "globalEnv": ["CI", "VERCEL"] }
-- affects this task's cache hash.env
-- affects all tasks' cache hashes.globalEnv
-- passed to task but excluded from hash (for secrets).passThroughEnv
Filtering
turbo run build --filter=web # by workspace name turbo run build --filter=@myorg/ui # by package name turbo run test --filter=./apps/* # by path glob turbo run build --filter=...@myorg/ui # package and its dependents turbo run build --filter=@myorg/ui... # package and its dependencies turbo run build --filter=...[HEAD~1] # changed since last commit turbo run test --filter=...[main...HEAD] # changed vs main branch turbo run build --filter=./apps/* --filter=!./apps/admin # exclude
Shared Packages
Internal UI library
packages/ui/package.json:
{ "name": "@myorg/ui", "main": "./src/index.ts", "types": "./src/index.ts", "scripts": { "build": "tsup src/index.ts --format esm,cjs --dts", "lint": "eslint src/" } }
Consume with
"@myorg/ui": "workspace:*" in the app's dependencies. For internal packages transpiled by the consuming bundler, point main at TypeScript source and skip the build step.
Shared TypeScript config
packages/tsconfig/base.json:
{ "compilerOptions": { "strict": true, "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", "declaration": true, "esModuleInterop": true, "skipLibCheck": true } }
Reference:
{ "extends": "@myorg/tsconfig/base.json", "include": ["src"] }
Shared types
packages/shared-types/package.json -- set "main" and "types" to "./src/index.ts". No build step needed if consumers transpile directly.
pnpm Workspaces Integration
pnpm-workspace.yaml:
packages: - "apps/*" - "packages/*"
Use
workspace:* protocol for internal deps. For npm workspaces, use the "workspaces" field in root package.json instead.
Watch Mode
turbo watch dev turbo watch build
Watches for file changes, re-runs affected tasks respecting the dependency graph. Shared package changes trigger downstream rebuilds.
Pruning for Docker
turbo prune web --docker
Produces
out/json/ (pruned package.json files for install layer) and out/full/ (pruned source).
FROM node:20-alpine AS builder RUN corepack enable WORKDIR /app COPY . . RUN turbo prune web --docker FROM node:20-alpine AS installer RUN corepack enable WORKDIR /app COPY --from=builder /app/out/json/ . RUN pnpm install --frozen-lockfile COPY --from=builder /app/out/full/ . RUN turbo run build --filter=web FROM node:20-alpine AS runner WORKDIR /app COPY --from=installer /app/apps/web/.next/standalone ./ CMD ["node", "apps/web/server.js"]
CI/CD Patterns
GitHub Actions with remote cache
name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: { node-version: 20, cache: pnpm } - run: pnpm install --frozen-lockfile - run: turbo run build test lint env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }}
Run only affected tasks:
turbo run build test --filter=...[origin/main...HEAD]
Use
turbo run build --dry-run=json to get the task graph for dispatching parallel CI jobs per workspace.
Migration from Lerna or Nx
From Lerna
- Remove
andlerna.json
dependency.lerna - Keep workspace config (
workspaces orpackage.json
).pnpm-workspace.yaml - Create
with task definitions matching Lerna scripts.turbo.json - Replace
withlerna run build
.turbo run build - Replace
withlerna run test --scope=pkg
.turbo run test --filter=pkg
From Nx
- Remove
,nx.json
files, andproject.json
dependencies.nx - Move task config from
targets intoproject.json
tasks.turbo.json - Replace
withnx run-many -t build
.turbo run build - Replace
withnx affected -t test
.turbo run test --filter=...[origin/main...HEAD]
Differences: Nx uses per-project
project.json and its own dependency analysis; Turbo uses a single root turbo.json and relies on the package manager's workspace graph. Nx has generators/executors; Turbo delegates to workspace package.json scripts.
Troubleshooting
turbo run build --dry-run # show what would run turbo run build --graph # output task graph (DOT format) turbo run build --summarize # generate run summary JSON turbo ls # list workspaces turbo ls --filter=./packages/* # list filtered workspaces TURBO_LOG_VERBOSITY=debug turbo run build # debug cache misses