askill
turborepo

turborepoSafety 98Repository

High-performance monorepo build system with intelligent caching, task orchestration, and parallel execution for multi-package repositories and microservices.

8 stars
1.2k downloads
Updated 1/23/2026

Package Files

Loading files...
SKILL.md

Turborepo 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: .turbo/cache directory
  • 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:

  • ^build: Run dependencies' build tasks first (topological)
  • build: Run local build task first
  • 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

  1. Start with single app in monorepo
  2. Extract shared utilities to packages/utils
  3. Extract UI components to packages/ui
  4. Add second app (admin, marketing, etc.)
  5. Extract common configs
  6. Set up remote caching
  7. Optimize CI/CD for monorepo

Comparison with Alternatives

Turborepo vs Nx

FeatureTurborepoNx
CachingContent-basedContent-based
Remote cacheVercel + customNx Cloud + custom
ComplexityMinimal configFeature-rich
Learning curveLowMedium-High
Code generationNoYes (generators)
Best forSimple monoreposEnterprise apps

Turborepo vs Lerna

FeatureTurborepoLerna
CachingAdvancedBasic
SpeedVery fastSlower
MaintenanceActiveMaintenance mode
FocusBuild systemPackage management
Best forModern monoreposLegacy projects

Turborepo vs Rush

FeatureTurborepoRush
Package managerAnypnpm-focused
ComplexitySimpleComplex
ConfigurationMinimalExtensive
Best forMost teamsLarge 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 ^build for topological dependencies
  • Configure outputs for all build artifacts
  • List only necessary env variables
  • Use cache: false for dev servers and watch modes
  • Set persistent: true for long-running processes

Caching Strategy

  • Enable remote cache for teams
  • Use .turbo/ in .gitignore
  • Don't cache non-deterministic outputs
  • Explicitly list all build outputs
  • Use --force to bypass cache when needed

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 .turbo/ directory
  • Use --filter=[origin/main] for changed packages
  • Set TURBO_TOKEN and TURBO_TEAM for remote cache
  • Use --frozen-lockfile for reproducible builds
  • Deploy only changed apps

TypeScript

  • Use project references for better type checking
  • Share tsconfig via config package
  • Enable composite: true for referenced projects
  • Configure paths for clean imports
  • Use tsup or tsup for library builds

Testing

  • Run tests in parallel: turbo run test
  • Use --filter to test changed packages
  • Configure test outputs for caching
  • Share test utilities in packages
  • Mock external dependencies

Performance

  • Profile with --profile flag
  • Monitor cache hit rate
  • Optimize outputs configuration
  • 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


This skill provides comprehensive Turborepo knowledge for building high-performance monorepos with intelligent caching and task orchestration.

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

98/100Analyzed 5 hours ago

Metadata

Licenseunknown
Version-
Updated1/23/2026
Publisherbobmatnyc

Tags

apici-cddatabasegithubgithub-actionslintingsecuritytesting