Awesome-omni-skill turborepo
High-performance monorepo build system with intelligent caching, task orchestration, and parallel execution for multi-package repositories and microservices.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/turborepo" ~/.claude/skills/diegosouzapw-awesome-omni-skill-turborepo && rm -rf "$T"
skills/development/turborepo/SKILL.mdTurborepo Skill
Summary
Turborepo is a high-performance monorepo build system with intelligent caching, task orchestration, and parallel execution. It provides fast incremental builds through content-aware hashing and remote caching, ideal for managing multiple packages, apps, and shared code.
When to Use
- Multi-package repositories with shared dependencies and utilities
- Microservices architectures requiring coordinated builds
- Component libraries shared across multiple applications
- Full-stack monorepos with frontend, backend, and shared packages
- Teams needing faster CI/CD through intelligent caching and parallelization
Quick Start
Installation and Setup
# Create new Turborepo (recommended) npx create-turbo@latest # Or add to existing monorepo npm install turbo --save-dev # Initialize Turborepo npx turbo init
Basic Structure
my-turborepo/ ├── apps/ │ ├── web/ # Next.js app │ └── api/ # Express API ├── packages/ │ ├── ui/ # Shared React components │ ├── config/ # Shared configs (eslint, tsconfig) │ └── utils/ # Shared utilities ├── turbo.json # Pipeline configuration └── package.json # Root package.json
Essential turbo.json
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"] }, "test": { "dependsOn": ["build"], "outputs": ["coverage/**"] }, "lint": { "outputs": [] }, "dev": { "cache": false, "persistent": true } } }
Run Tasks
# Build all packages and apps turbo run build # Run tests across workspace turbo run test # Run tasks in parallel turbo run lint test build # Filter by package turbo run build --filter=web turbo run test --filter=@myorg/ui # Run in specific workspace turbo run dev --filter=web...
Core Concepts
Monorepo Architecture
Turborepo manages multiple packages in a single repository:
- apps/: Application projects (Next.js, Express, etc.)
- packages/: Shared libraries and utilities
- Workspace dependencies: Internal package references
- Single version control: Unified commits and history
Task Caching
Content-aware hashing for intelligent cache invalidation:
{ "pipeline": { "build": { "outputs": ["dist/**", ".next/**"], "inputs": ["src/**", "package.json"] } } }
- Cache hits: Instant task completion (0ms)
- Local cache:
directory.turbo/cache - Remote cache: Shared across team (Vercel, custom)
- Cache invalidation: Automatic on file changes
Pipeline Configuration
Define task dependencies and execution order:
{ "pipeline": { "build": { "dependsOn": ["^build"], // Dependencies first "outputs": ["dist/**"] }, "test": { "dependsOn": ["build"], // After local build "outputs": ["coverage/**"] }, "deploy": { "dependsOn": ["build", "test"], // Multiple dependencies "outputs": [] } } }
Dependency Operators:
: Run dependencies' build tasks first (topological)^build
: Run local build task firstbuild- No prefix: Run in parallel
Project Structure
Complete Monorepo Example
turborepo-example/ ├── apps/ │ ├── web/ # Next.js app │ │ ├── src/ │ │ ├── package.json │ │ └── tsconfig.json │ ├── admin/ # Admin dashboard │ │ ├── src/ │ │ └── package.json │ └── api/ # Express API │ ├── src/ │ └── package.json ├── packages/ │ ├── ui/ # Component library │ │ ├── src/ │ │ │ ├── Button.tsx │ │ │ ├── Input.tsx │ │ │ └── index.ts │ │ ├── package.json │ │ └── tsconfig.json │ ├── config/ # Shared configs │ │ ├── eslint-config/ │ │ ├── tsconfig/ │ │ └── tailwind-config/ │ ├── utils/ # Shared utilities │ │ ├── src/ │ │ │ ├── format.ts │ │ │ ├── validators.ts │ │ │ └── index.ts │ │ └── package.json │ └── types/ # Shared TypeScript types │ ├── src/ │ └── package.json ├── .turbo/ # Cache directory ├── turbo.json # Pipeline config ├── package.json # Root package ├── pnpm-workspace.yaml # Workspace definition └── tsconfig.json # Root TypeScript config
Workspace Configuration
Root package.json:
{ "name": "my-turborepo", "private": true, "workspaces": ["apps/*", "packages/*"], "scripts": { "build": "turbo run build", "dev": "turbo run dev", "test": "turbo run test", "lint": "turbo run lint", "clean": "turbo run clean && rm -rf node_modules .turbo" }, "devDependencies": { "turbo": "latest" }, "engines": { "node": ">=18", "pnpm": ">=8" }, "packageManager": "pnpm@8.10.0" }
pnpm-workspace.yaml:
packages: - "apps/*" - "packages/*"
Package Manager Integration
pnpm (Recommended)
Fast, efficient, and workspace-native:
# Install dependencies pnpm install # Add dependency to specific package pnpm add react --filter=web pnpm add @myorg/ui --filter=admin # Add workspace dependency cd apps/web pnpm add @myorg/ui@workspace:* # Run scripts pnpm turbo run build
pnpm-workspace.yaml:
packages: - "apps/*" - "packages/*"
npm Workspaces
{ "workspaces": ["apps/*", "packages/*"] }
npm install npm run build --workspace=web npm run test --workspaces
Yarn Workspaces
{ "workspaces": { "packages": ["apps/*", "packages/*"] } }
yarn install yarn workspace web build yarn workspaces run test
turbo.json Configuration
Complete Configuration
{ "$schema": "https://turbo.build/schema.json", "globalDependencies": [".env", "tsconfig.json"], "globalEnv": ["NODE_ENV", "API_URL"], "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "build/**"], "env": ["NEXT_PUBLIC_API_URL"], "outputMode": "new-only" }, "test": { "dependsOn": ["build"], "outputs": ["coverage/**"], "inputs": ["src/**", "test/**", "__tests__/**"] }, "test:unit": { "dependsOn": [], "outputs": [], "cache": false }, "lint": { "dependsOn": [], "outputs": [] }, "type-check": { "dependsOn": [], "outputs": [] }, "dev": { "cache": false, "persistent": true }, "clean": { "cache": false } } }
Pipeline Options
dependsOn: Task dependencies
{ "build": { "dependsOn": ["^build"] // Run dependencies' build first }, "test": { "dependsOn": ["build"] // Run local build first }, "deploy": { "dependsOn": ["build", "test"] // Multiple tasks } }
outputs: Files to cache
{ "build": { "outputs": [ "dist/**", // Output directory ".next/**", // Next.js build "!.next/cache/**" // Exclude cache ] } }
inputs: Files that invalidate cache
{ "build": { "inputs": [ "src/**", // Source files "package.json", // Dependencies "tsconfig.json" // Config files ] } }
env: Environment variables affecting cache
{ "build": { "env": [ "NEXT_PUBLIC_API_URL", "DATABASE_URL" ] } }
outputMode: Control console output
{ "build": { "outputMode": "new-only" // Only show new output // "full" | "hash-only" | "new-only" | "errors-only" | "none" } }
cache: Enable/disable caching
{ "dev": { "cache": false, // Don't cache dev server "persistent": true // Keep process running } }
Global Configuration
globalDependencies: Files affecting all tasks
{ "globalDependencies": [ ".env", "tsconfig.json", "package.json" ] }
globalEnv: Environment variables for all tasks
{ "globalEnv": [ "NODE_ENV", "CI", "VERCEL" ] }
Task Execution
Running Tasks
# Run single task turbo run build turbo run test turbo run lint # Run multiple tasks turbo run build test lint # Run in specific order (sequential) turbo run build && turbo run test && turbo run deploy
Filtering and Scoping
Filter by package:
# Single package turbo run build --filter=web turbo run test --filter=@myorg/ui # Multiple packages turbo run build --filter=web --filter=admin turbo run build --filter={web,admin}
Include dependencies:
# Package and its dependencies turbo run build --filter=web... # Package and its dependents turbo run test --filter=...@myorg/ui
Filter by directory:
# All apps turbo run build --filter=./apps/* # Specific directory turbo run test --filter=./packages/ui
Filter by git changes:
# Changed since main turbo run build --filter=[main] # Changed in last commit turbo run test --filter=[HEAD^1] # Changed packages only turbo run lint --filter=[origin/main]
Execution Options
Parallel execution:
# Control concurrency turbo run build --concurrency=4 turbo run test --concurrency=50% # Unlimited parallel (default) turbo run lint --concurrency=100%
Force execution (bypass cache):
turbo run build --force turbo run test --force
Continue on error:
turbo run test --continue
Output modes:
turbo run build --output-logs=new-only turbo run test --output-logs=errors-only turbo run lint --output-logs=full
Dry run:
turbo run build --dry turbo run build --dry=json
Environment modes:
turbo run build --env-mode=strict # Error on missing env vars turbo run build --env-mode=loose # Allow missing env vars
Caching
Local Cache
Automatic file-based caching in
.turbo/cache:
# First run: Full execution turbo run build # >>> FULL TURBO # >>> build: cache miss, executing... # Second run: Cache hit turbo run build # >>> FULL TURBO # >>> build: cache hit, replaying output...
Cache structure:
.turbo/ ├── cache/ │ ├── abc123.tar.zst # Compressed outputs │ └── def456.tar.zst └── runs/ └── xyz789.json # Run metadata
Remote Cache (Vercel)
Share cache across team and CI:
# Link to Vercel npx turbo login npx turbo link # Runs will now use remote cache turbo run build # >>> Remote caching enabled
turbo.json with remote cache:
{ "remoteCache": { "enabled": true } }
Custom Remote Cache
Self-hosted cache server:
# Configure custom cache turbo run build --api="https://cache.mycompany.com" --token="${TURBO_TOKEN}"
Environment variables:
export TURBO_API="https://cache.mycompany.com" export TURBO_TOKEN="your-secret-token" export TURBO_TEAM="team-id"
Cache Configuration
Disable caching:
{ "pipeline": { "dev": { "cache": false } } }
Cache signatures (what affects cache):
- Input files (source code, configs)
- Environment variables (env)
- Dependencies' outputs (dependsOn)
- Global dependencies (globalDependencies)
- Task configuration (turbo.json)
Invalidate cache:
# Delete local cache rm -rf .turbo/cache # Force rebuild turbo run build --force
Code Sharing
Internal Packages
Shared component library:
packages/ui/ ├── src/ │ ├── Button.tsx │ ├── Input.tsx │ └── index.ts ├── package.json └── tsconfig.json
packages/ui/package.json:
{ "name": "@myorg/ui", "version": "0.0.0", "main": "./src/index.ts", "types": "./src/index.ts", "exports": { ".": "./src/index.ts", "./button": "./src/Button.tsx" }, "scripts": { "lint": "eslint src/", "type-check": "tsc --noEmit" }, "devDependencies": { "react": "^18.0.0", "typescript": "^5.0.0" } }
packages/ui/src/index.ts:
export { Button } from './Button'; export { Input } from './Input'; export type { ButtonProps, InputProps } from './types';
Using in apps:
{ "dependencies": { "@myorg/ui": "workspace:*" } }
import { Button, Input } from '@myorg/ui';
Shared Configuration
ESLint config package:
packages/config/eslint-config/ ├── index.js ├── package.json └── README.md
package.json:
{ "name": "@myorg/eslint-config", "version": "0.0.0", "main": "index.js", "dependencies": { "eslint": "^8.0.0", "eslint-config-next": "^14.0.0" } }
index.js:
module.exports = { extends: ['next', 'prettier'], rules: { '@next/next/no-html-link-for-pages': 'off', }, };
Using in apps:
{ "devDependencies": { "@myorg/eslint-config": "workspace:*" } }
.eslintrc.js:
module.exports = { extends: ['@myorg/eslint-config'], };
TypeScript Configuration
Shared tsconfig:
packages/config/tsconfig/ ├── base.json ├── nextjs.json ├── react-library.json └── package.json
base.json:
{ "compilerOptions": { "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "incremental": true } }
nextjs.json:
{ "extends": "./base.json", "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], "jsx": "preserve", "module": "esnext", "noEmit": true } }
Using in apps:
{ "extends": "@myorg/tsconfig/nextjs.json", "compilerOptions": { "outDir": "dist" }, "include": ["src"], "exclude": ["node_modules"] }
TypeScript Project References
Root tsconfig.json:
{ "files": [], "references": [ { "path": "./apps/web" }, { "path": "./apps/admin" }, { "path": "./packages/ui" }, { "path": "./packages/utils" } ] }
Package tsconfig.json:
{ "extends": "@myorg/tsconfig/react-library.json", "compilerOptions": { "composite": true, "outDir": "dist", "rootDir": "src" }, "include": ["src"], "references": [ { "path": "../utils" } ] }
Build with references:
tsc --build turbo run type-check
Integration Patterns
Next.js in Monorepo
apps/web/package.json:
{ "name": "web", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "next": "^14.0.0", "react": "^18.0.0", "@myorg/ui": "workspace:*", "@myorg/utils": "workspace:*" } }
apps/web/next.config.js:
/** @type {import('next').NextConfig} */ module.exports = { transpilePackages: ['@myorg/ui', '@myorg/utils'], reactStrictMode: true, };
turbo.json:
{ "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**"] }, "dev": { "cache": false, "persistent": true } } }
React Component Library
packages/ui/package.json:
{ "name": "@myorg/ui", "version": "0.0.0", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.js", "types": "./dist/index.d.ts" } }, "scripts": { "build": "tsup src/index.ts --format cjs,esm --dts", "dev": "tsup src/index.ts --format cjs,esm --dts --watch", "lint": "eslint src/" }, "devDependencies": { "tsup": "^8.0.0", "typescript": "^5.0.0", "react": "^18.0.0" }, "peerDependencies": { "react": "^18.0.0" } }
packages/ui/tsup.config.ts:
import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], dts: true, sourcemap: true, clean: true, external: ['react'], });
API Packages
packages/api-client/package.json:
{ "name": "@myorg/api-client", "scripts": { "build": "tsup src/index.ts --format cjs,esm --dts", "test": "vitest run", "type-check": "tsc --noEmit" }, "dependencies": { "@myorg/types": "workspace:*" }, "devDependencies": { "tsup": "^8.0.0", "vitest": "^1.0.0" } }
src/client.ts:
import type { User, Post } from '@myorg/types'; export class APIClient { constructor(private baseUrl: string) {} async getUser(id: string): Promise<User> { const response = await fetch(`${this.baseUrl}/users/${id}`); return response.json(); } async getPosts(): Promise<Post[]> { const response = await fetch(`${this.baseUrl}/posts`); return response.json(); } }
Docker Integration
Pruned Builds
Build only what you need:
FROM node:18-alpine AS base RUN npm install -g turbo pnpm FROM base AS builder WORKDIR /app COPY . . # Prune workspace to only include 'web' app RUN turbo prune --scope=web --docker FROM base AS installer WORKDIR /app # Copy pruned workspace COPY --from=builder /app/out/json/ . COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml RUN pnpm install --frozen-lockfile COPY --from=builder /app/out/full/ . RUN pnpm turbo run build --filter=web... FROM base AS runner WORKDIR /app COPY --from=installer /app/apps/web/.next/standalone ./ COPY --from=installer /app/apps/web/.next/static ./apps/web/.next/static COPY --from=installer /app/apps/web/public ./apps/web/public EXPOSE 3000 CMD ["node", "apps/web/server.js"]
Multi-App Docker
# Build stage with pruning FROM node:18-alpine AS builder RUN npm install -g turbo pnpm WORKDIR /app COPY . . # Prune for specific app (passed as build arg) ARG APP RUN turbo prune --scope=${APP} --docker # Install dependencies FROM node:18-alpine AS installer WORKDIR /app COPY --from=builder /app/out/json/ . COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml RUN pnpm install --frozen-lockfile # Build COPY --from=builder /app/out/full/ . ARG APP RUN pnpm turbo run build --filter=${APP}... # Runtime FROM node:18-alpine AS runner ARG APP WORKDIR /app COPY --from=installer /app . CMD pnpm --filter=${APP} start
Build:
docker build --build-arg APP=web -t my-web-app . docker build --build-arg APP=admin -t my-admin-app .
Docker Compose
version: '3.8' services: web: build: context: . dockerfile: Dockerfile args: APP: web ports: - "3000:3000" environment: - NODE_ENV=production - API_URL=http://api:3001 admin: build: context: . dockerfile: Dockerfile args: APP: admin ports: - "3001:3000" environment: - NODE_ENV=production api: build: context: . dockerfile: apps/api/Dockerfile ports: - "3001:3001" environment: - DATABASE_URL=postgres://db:5432/mydb db: image: postgres:15 environment: POSTGRES_DB: mydb POSTGRES_PASSWORD: secret
CI/CD Integration
GitHub Actions
name: CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/setup-node@v4 with: node-version: 18 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build run: pnpm turbo run build - name: Test run: pnpm turbo run test - name: Lint run: pnpm turbo run lint
With Turborepo Remote Cache
name: CI on: push: branches: [main] pull_request: branches: [main] env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/setup-node@v4 with: node-version: 18 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build and test run: pnpm turbo run build test lint --cache-dir=.turbo - name: Upload cache uses: actions/cache@v3 with: path: .turbo key: ${{ runner.os }}-turbo-${{ github.sha }} restore-keys: | ${{ runner.os }}-turbo-
Changed Files Only
name: CI on: pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/setup-node@v4 with: node-version: 18 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build changed packages run: pnpm turbo run build --filter=[origin/main] - name: Test changed packages run: pnpm turbo run test --filter=[origin/main]
Deployment Strategies
Vercel Deployment
Automatic deployment with remote cache:
{ "buildCommand": "turbo run build", "framework": "nextjs", "installCommand": "pnpm install", "outputDirectory": "apps/web/.next" }
vercel.json:
{ "buildCommand": "turbo run build --filter=web", "installCommand": "pnpm install --frozen-lockfile", "framework": null, "outputDirectory": "apps/web/.next" }
Deploy Affected Apps
# Get changed apps since last deploy CHANGED_APPS=$(turbo run build --filter=[origin/main] --dry=json | jq -r '.packages[]') # Deploy each changed app for app in $CHANGED_APPS; do echo "Deploying $app..." # Your deployment command done
GitHub Actions example:
- name: Get changed apps id: changed run: | CHANGED=$(pnpm turbo run build --filter=[origin/main] --dry=json | jq -r '.packages | join(",")') echo "apps=$CHANGED" >> $GITHUB_OUTPUT - name: Deploy web if: contains(steps.changed.outputs.apps, 'web') run: vercel deploy --prod apps/web - name: Deploy admin if: contains(steps.changed.outputs.apps, 'admin') run: vercel deploy --prod apps/admin
Docker Deployment
name: Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build and push Docker image run: | docker build --build-arg APP=web -t myregistry/web:latest . docker push myregistry/web:latest - name: Deploy to Kubernetes run: | kubectl set image deployment/web web=myregistry/web:latest
Migration from Single Repo
Step-by-Step Migration
1. Initialize Turborepo:
# In existing repo npm install turbo --save-dev npx turbo init
2. Create workspace structure:
mkdir -p apps/web packages/ui
3. Move existing app:
# Move current code to apps/web git mv src apps/web/src git mv public apps/web/public git mv package.json apps/web/package.json
4. Update root package.json:
{ "name": "monorepo", "private": true, "workspaces": ["apps/*", "packages/*"], "scripts": { "build": "turbo run build", "dev": "turbo run dev", "test": "turbo run test" }, "devDependencies": { "turbo": "latest" } }
5. Configure turbo.json:
{ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"] }, "dev": { "cache": false, "persistent": true } } }
6. Extract shared code:
# Create shared package mkdir -p packages/ui/src # Move components mv apps/web/src/components packages/ui/src/ # Update imports in apps/web
7. Install dependencies:
pnpm install
8. Test:
pnpm turbo run build pnpm turbo run dev
Gradual Migration Strategy
- Start with single app in monorepo
- Extract shared utilities to
packages/utils - Extract UI components to
packages/ui - Add second app (admin, marketing, etc.)
- Extract common configs
- Set up remote caching
- Optimize CI/CD for monorepo
Comparison with Alternatives
Turborepo vs Nx
| Feature | Turborepo | Nx |
|---|---|---|
| Caching | Content-based | Content-based |
| Remote cache | Vercel + custom | Nx Cloud + custom |
| Complexity | Minimal config | Feature-rich |
| Learning curve | Low | Medium-High |
| Code generation | No | Yes (generators) |
| Best for | Simple monorepos | Enterprise apps |
Turborepo vs Lerna
| Feature | Turborepo | Lerna |
|---|---|---|
| Caching | Advanced | Basic |
| Speed | Very fast | Slower |
| Maintenance | Active | Maintenance mode |
| Focus | Build system | Package management |
| Best for | Modern monorepos | Legacy projects |
Turborepo vs Rush
| Feature | Turborepo | Rush |
|---|---|---|
| Package manager | Any | pnpm-focused |
| Complexity | Simple | Complex |
| Configuration | Minimal | Extensive |
| Best for | Most teams | Large enterprises |
When to choose Turborepo:
- Want simple, fast monorepo builds
- Need remote caching out of the box
- Using Vercel ecosystem
- Prefer minimal configuration
- Small to medium teams
When to consider alternatives:
- Need code generation (Nx)
- Want plugin ecosystem (Nx)
- Very large enterprise (Rush)
- Legacy Lerna project (stay with Lerna)
Performance Optimization
Cache Optimization
{ "pipeline": { "build": { "outputs": [ "dist/**", ".next/**", "!**/*.map", // Exclude source maps "!.next/cache/**" // Exclude Next.js cache ] } } }
Parallel Execution
# Limit concurrency for memory-constrained environments turbo run build --concurrency=2 # Use all cores turbo run test --concurrency=100% # Specific number turbo run lint --concurrency=8
Selective Execution
# Only changed packages turbo run build --filter=[HEAD^1] # Package and dependencies turbo run build --filter=web... # Exclude packages turbo run test --filter=!admin
Environment Variable Optimization
{ "globalEnv": ["NODE_ENV", "CI"], "pipeline": { "build": { "env": [ "NEXT_PUBLIC_API_URL", "DATABASE_URL" ] } } }
Only list env vars that affect output - unnecessary env vars invalidate cache.
Dependency Optimization
{ "pipeline": { "build": { "dependsOn": ["^build"], // Necessary "inputs": [ "src/**/*.{ts,tsx}", // Source only "!**/__tests__/**" // Exclude tests ] } } }
Troubleshooting
Cache Not Working
Check cache hits:
turbo run build --dry=json | jq '.tasks[] | {task: .taskId, cache: .cache}'
Common causes:
- Environment variables not declared in
env - Outputs not properly configured
- Global dependencies changing
- Non-deterministic builds (timestamps, random data)
Debug caching:
# Force execution turbo run build --force # Check what invalidated cache turbo run build --output-logs=hash-only
Build Failures
Dependency issues:
# Clear and reinstall rm -rf node_modules .turbo pnpm install --frozen-lockfile
TypeScript errors:
# Check project references tsc --build --force # Type check all packages turbo run type-check
Performance Issues
Profile builds:
turbo run build --profile
Check task graph:
turbo run build --graph
Generates
graph.png showing task dependencies.
Optimize concurrency:
# Too many parallel tasks can exhaust memory turbo run build --concurrency=4
Remote Cache Not Working
Check authentication:
npx turbo login npx turbo link
Verify environment variables:
echo $TURBO_TOKEN echo $TURBO_TEAM
Test connection:
turbo run build --remote-only
Best Practices
Project Organization
- apps/: Application code (Next.js, Express, etc.)
- packages/: Shared libraries and utilities
- config/: Shared configurations (ESLint, TypeScript, Tailwind)
- Keep apps independent - avoid direct imports between apps
- Use workspace protocol:
"@myorg/ui": "workspace:*"
Pipeline Configuration
- Use
for topological dependencies^build - Configure
for all build artifactsoutputs - List only necessary
variablesenv - Use
for dev servers and watch modescache: false - Set
for long-running processespersistent: true
Caching Strategy
- Enable remote cache for teams
- Use
in.turbo/.gitignore - Don't cache non-deterministic outputs
- Explicitly list all build outputs
- Use
to bypass cache when needed--force
Workspace Dependencies
- Use workspace protocol:
workspace:* - Keep internal packages versioned at
0.0.0 - Configure package exports properly
- Use TypeScript project references for type checking
- Transpile packages in consuming apps (Next.js)
CI/CD
- Cache
directory.turbo/ - Use
for changed packages--filter=[origin/main] - Set
andTURBO_TOKEN
for remote cacheTURBO_TEAM - Use
for reproducible builds--frozen-lockfile - Deploy only changed apps
TypeScript
- Use project references for better type checking
- Share tsconfig via config package
- Enable
for referenced projectscomposite: true - Configure
for clean importspaths - Use
ortsup
for library buildstsup
Testing
- Run tests in parallel:
turbo run test - Use
to test changed packages--filter - Configure test outputs for caching
- Share test utilities in packages
- Mock external dependencies
Performance
- Profile with
flag--profile - Monitor cache hit rate
- Optimize
configurationoutputs - Use concurrency limits on CI
- Enable remote caching
Code Sharing
- Extract common utilities early
- Create focused, single-purpose packages
- Document package APIs
- Version internal packages together
- Use peer dependencies for React, etc.
Advanced Patterns
Conditional Outputs
{ "pipeline": { "build": { "outputs": [ "dist/**", ".next/**", "{projectRoot}/build/**" // Project-specific ] } } }
Task-Specific Environment
{ "pipeline": { "build:production": { "dependsOn": ["^build"], "env": ["NODE_ENV", "API_URL"], "outputs": ["dist/**"] }, "build:staging": { "dependsOn": ["^build"], "env": ["NODE_ENV", "STAGING_API_URL"], "outputs": ["dist/**"] } } }
Workspace Filtering Examples
# Build web and its dependencies turbo run build --filter=web... # Build everything that depends on ui turbo run build --filter=...ui # Build changed packages and dependents turbo run build --filter=[main]... # Build specific scope turbo run build --filter=@myorg/* # Exclude packages turbo run test --filter=!admin --filter=!legacy
Multiple Entry Points
{ "name": "@myorg/ui", "exports": { ".": "./src/index.ts", "./button": "./src/button.tsx", "./input": "./src/input.tsx", "./hooks": "./src/hooks/index.ts" } }
import { Button } from '@myorg/ui/button'; import { useDebounce } from '@myorg/ui/hooks';
Shared Build Scripts
packages/scripts/ ├── build.js ├── test.js └── package.json
build.js:
#!/usr/bin/env node const { build } = require('tsup'); build({ entry: ['src/index.ts'], format: ['cjs', 'esm'], dts: true, sourcemap: true, clean: true, }).catch((error) => { console.error(error); process.exit(1); });
Using in packages:
{ "scripts": { "build": "node ../../packages/scripts/build.js" } }
Resources
Official Documentation
Community
Related Tools
- pnpm - Fast package manager
- tsup - TypeScript bundler
- changesets - Version management
- Vercel - Deployment platform
This skill provides comprehensive Turborepo knowledge for building high-performance monorepos with intelligent caching and task orchestration.