turborepo-caching

Turborepo configuration for efficient monorepo builds with local and remote caching strategies. Configure pipeline tasks with dependency graphs, output caching, and environment-specific inputs to optimize build performance across workspaces Supports local caching, Vercel remote caching, and self-hosted cache servers with filtering to build only affected packages Includes task-level configuration for persistent processes (dev servers), cache exclusions, and package-specific pipeline overrides Provides filtering syntax to scope builds by package, dependencies, changed files, and directory patterns for targeted CI/CD execution

INSTALLATION
npx skills add https://github.com/wshobson/agents --skill turborepo-caching
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Turborepo Caching

Production patterns for Turborepo build optimization.

When to Use This Skill

  • Setting up new Turborepo projects
  • Configuring build pipelines
  • Implementing remote caching
  • Optimizing CI/CD performance
  • Migrating from other monorepo tools
  • Debugging cache misses

Core Concepts

1. Turborepo Architecture

Workspace Root/

├── apps/

│   ├── web/

│   │   └── package.json

│   └── docs/

│       └── package.json

├── packages/

│   ├── ui/

│   │   └── package.json

│   └── config/

│       └── package.json

├── turbo.json

└── package.json

2. Pipeline Concepts

Concept

Description

dependsOn

Tasks that must complete first

cache

Whether to cache outputs

outputs

Files to cache

inputs

Files that affect cache key

persistent

Long-running tasks (dev servers)

Templates

Template 1: turbo.json Configuration

{

  "$schema": "https://turbo.build/schema.json",

  "globalDependencies": [".env", ".env.local"],

  "globalEnv": ["NODE_ENV", "VERCEL_URL"],

  "pipeline": {

    "build": {

      "dependsOn": ["^build"],

      "outputs": ["dist/**", ".next/**", "!.next/cache/**"],

      "env": ["API_URL", "NEXT_PUBLIC_*"]

    },

    "test": {

      "dependsOn": ["build"],

      "outputs": ["coverage/**"],

      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]

    },

    "lint": {

      "outputs": [],

      "cache": true

    },

    "typecheck": {

      "dependsOn": ["^build"],

      "outputs": []

    },

    "dev": {

      "cache": false,

      "persistent": true

    },

    "clean": {

      "cache": false

    }

  }

}

Template 2: Package-Specific Pipeline

// apps/web/turbo.json

{

  "$schema": "https://turbo.build/schema.json",

  "extends": ["//"],

  "pipeline": {

    "build": {

      "outputs": [".next/**", "!.next/cache/**"],

      "env": ["NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_ANALYTICS_ID"]

    },

    "test": {

      "outputs": ["coverage/**"],

      "inputs": ["src/**", "tests/**", "jest.config.js"]

    }

  }

}

Template 3: Remote Caching with Vercel

# Login to Vercel

npx turbo login

# Link to Vercel project

npx turbo link

# Run with remote cache

turbo build --remote-only

# CI environment variables

TURBO_TOKEN=your-token

TURBO_TEAM=your-team
# .github/workflows/ci.yml

name: CI

on:

  push:

    branches: [main]

  pull_request:

env:

  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

  TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:

  build:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4

        with:

          node-version: 20

          cache: "npm"

      - name: Install dependencies

        run: npm ci

      - name: Build

        run: npx turbo build --filter='...[origin/main]'

      - name: Test

        run: npx turbo test --filter='...[origin/main]'

Template 4: Self-Hosted Remote Cache

// Custom remote cache server (Express)

import express from "express";

import { createReadStream, createWriteStream } from "fs";

import { mkdir } from "fs/promises";

import { join } from "path";

const app = express();

const CACHE_DIR = "./cache";

// Get artifact

app.get("/v8/artifacts/:hash", async (req, res) => {

  const { hash } = req.params;

  const team = req.query.teamId || "default";

  const filePath = join(CACHE_DIR, team, hash);

  try {

    const stream = createReadStream(filePath);

    stream.pipe(res);

  } catch {

    res.status(404).send("Not found");

  }

});

// Put artifact

app.put("/v8/artifacts/:hash", async (req, res) => {

  const { hash } = req.params;

  const team = req.query.teamId || "default";

  const dir = join(CACHE_DIR, team);

  const filePath = join(dir, hash);

  await mkdir(dir, { recursive: true });

  const stream = createWriteStream(filePath);

  req.pipe(stream);

  stream.on("finish", () => {

    res.json({

      urls: [`${req.protocol}://${req.get("host")}/v8/artifacts/${hash}`],

    });

  });

});

// Check artifact exists

app.head("/v8/artifacts/:hash", async (req, res) => {

  const { hash } = req.params;

  const team = req.query.teamId || "default";

  const filePath = join(CACHE_DIR, team, hash);

  try {

    await fs.access(filePath);

    res.status(200).end();

  } catch {

    res.status(404).end();

  }

});

app.listen(3000);
// turbo.json for self-hosted cache

{

  "remoteCache": {

    "signature": false

  }

}
# Use self-hosted cache

turbo build --api="http://localhost:3000" --token="my-token" --team="my-team"

Template 5: Filtering and Scoping

# Build specific package

turbo build --filter=@myorg/web

# Build package and its dependencies

turbo build --filter=@myorg/web...

# Build package and its dependents

turbo build --filter=...@myorg/ui

# Build changed packages since main

turbo build --filter='...[origin/main]'

# Build packages in directory

turbo build --filter='./apps/*'

# Combine filters

turbo build --filter=@myorg/web --filter=@myorg/docs

# Exclude package

turbo build --filter='!@myorg/docs'

# Include dependencies of changed

turbo build --filter='...[HEAD^1]...'

Template 6: Advanced Pipeline Configuration

{

  "$schema": "https://turbo.build/schema.json",

  "pipeline": {

    "build": {

      "dependsOn": ["^build"],

      "outputs": ["dist/**"],

      "inputs": ["$TURBO_DEFAULT$", "!**/*.md", "!**/*.test.*"]

    },

    "test": {

      "dependsOn": ["^build"],

      "outputs": ["coverage/**"],

      "inputs": ["src/**", "tests/**", "*.config.*"],

      "env": ["CI", "NODE_ENV"]

    },

    "test:e2e": {

      "dependsOn": ["build"],

      "outputs": [],

      "cache": false

    },

    "deploy": {

      "dependsOn": ["build", "test", "lint"],

      "outputs": [],

      "cache": false

    },

    "db:generate": {

      "cache": false

    },

    "db:push": {

      "cache": false,

      "dependsOn": ["db:generate"]

    },

    "@myorg/web#build": {

      "dependsOn": ["^build", "@myorg/db#db:generate"],

      "outputs": [".next/**"],

      "env": ["NEXT_PUBLIC_*"]

    }

  }

}

Template 7: Root package.json Setup

{

  "name": "my-turborepo",

  "private": true,

  "workspaces": ["apps/*", "packages/*"],

  "scripts": {

    "build": "turbo build",

    "dev": "turbo dev",

    "lint": "turbo lint",

    "test": "turbo test",

    "clean": "turbo clean && rm -rf node_modules",

    "format": "prettier --write \"**/*.{ts,tsx,md}\"",

    "changeset": "changeset",

    "version-packages": "changeset version",

    "release": "turbo build --filter=./packages/* && changeset publish"

  },

  "devDependencies": {

    "turbo": "^1.10.0",

    "prettier": "^3.0.0",

    "@changesets/cli": "^2.26.0"

  },

  "packageManager": "npm@10.0.0"

}

Debugging Cache

# Dry run to see what would run

turbo build --dry-run

# Verbose output with hashes

turbo build --verbosity=2

# Show task graph

turbo build --graph

# Force no cache

turbo build --force

# Show cache status

turbo build --summarize

# Debug specific task

TURBO_LOG_VERBOSITY=debug turbo build --filter=@myorg/web

Best Practices

Do's

  • Define explicit inputs - Avoid cache invalidation
  • Use workspace protocol - "@myorg/ui": "workspace:*"
  • Enable remote caching - Share across CI and local
  • Filter in CI - Build only affected packages
  • Cache build outputs - Not source files

Don'ts

  • Don't cache dev servers - Use persistent: true
  • Don't include secrets in env - Use runtime env vars
  • Don't ignore dependsOn - Causes race conditions
  • Don't over-filter - May miss dependencies
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card