turborepo-caching

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Turborepo Caching

Turborepo 缓存

Production patterns for Turborepo build optimization.
用于Turborepo构建优化的生产级实践方案。

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
  • 搭建新的Turborepo项目
  • 配置构建流水线
  • 实现远程缓存
  • 优化CI/CD性能
  • 从其他单体仓库工具迁移
  • 调试缓存未命中问题

Core Concepts

核心概念

1. Turborepo Architecture

1. Turborepo 架构

Workspace Root/
├── apps/
│   ├── web/
│   │   └── package.json
│   └── docs/
│       └── package.json
├── packages/
│   ├── ui/
│   │   └── package.json
│   └── config/
│       └── package.json
├── turbo.json
└── package.json
Workspace Root/
├── apps/
│   ├── web/
│   │   └── package.json
│   └── docs/
│       └── package.json
├── packages/
│   ├── ui/
│   │   └── package.json
│   └── config/
│       └── package.json
├── turbo.json
└── package.json

2. Pipeline Concepts

2. 流水线概念

ConceptDescription
dependsOnTasks that must complete first
cacheWhether to cache outputs
outputsFiles to cache
inputsFiles that affect cache key
persistentLong-running tasks (dev servers)
Concept描述
dependsOn必须先完成的任务
cache是否缓存输出结果
outputs需要缓存的文件
inputs影响缓存键的文件
persistent长期运行的任务(如开发服务器)

Templates

模板

Template 1: turbo.json Configuration

模板1:turbo.json 配置

json
{
  "$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
    }
  }
}
json
{
  "$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

模板2:包专属流水线配置

json
// 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"]
    }
  }
}
json
// 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

模板3:结合Vercel实现远程缓存

bash
undefined
bash
undefined

Login to Vercel

登录Vercel

npx turbo login
npx turbo login

Link to Vercel project

关联到Vercel项目

npx turbo link
npx turbo link

Run with remote cache

使用远程缓存运行构建

turbo build --remote-only
turbo build --remote-only

CI environment variables

CI环境变量

TURBO_TOKEN=your-token TURBO_TEAM=your-team

```yaml
TURBO_TOKEN=your-token TURBO_TEAM=your-team

```yaml

.github/workflows/ci.yml

.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]'
undefined
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]'
undefined

Template 4: Self-Hosted Remote Cache

模板4:自建远程缓存服务

typescript
// 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);
json
// turbo.json for self-hosted cache
{
  "remoteCache": {
    "signature": false
  }
}
bash
undefined
typescript
// 自定义远程缓存服务器(基于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";

// 获取构件
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");
  }
});

// 上传构件
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}`],
    });
  });
});

// 检查构件是否存在
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);
json
// 适配自建缓存的turbo.json配置
{
  "remoteCache": {
    "signature": false
  }
}
bash
undefined

Use self-hosted cache

使用自建缓存服务运行构建

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

Template 5: Filtering and Scoping

模板5:过滤与范围指定

bash
undefined
bash
undefined

Build specific package

构建指定包

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

Build package and its dependencies

构建指定包及其依赖

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

Build package and its dependents

构建指定包的所有依赖项

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

Build changed packages since main

构建自main分支以来有变更的包

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

Build packages in directory

构建指定目录下的包

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

Combine filters

组合过滤条件

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

Exclude package

排除指定包

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

Include dependencies of changed

包含变更包的依赖项

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

Template 6: Advanced Pipeline Configuration

模板6:高级流水线配置

json
{
  "$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_*"]
    }
  }
}
json
{
  "$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

模板7:根目录package.json配置

json
{
  "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"
}
json
{
  "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

缓存调试

bash
undefined
bash
undefined

Dry run to see what would run

空运行,查看会执行哪些任务

turbo build --dry-run
turbo build --dry-run

Verbose output with hashes

输出详细日志与哈希值

turbo build --verbosity=2
turbo build --verbosity=2

Show task graph

展示任务依赖图

turbo build --graph
turbo build --graph

Force no cache

强制不使用缓存运行构建

turbo build --force
turbo build --force

Show cache status

展示缓存状态

turbo build --summarize
turbo build --summarize

Debug specific task

调试指定任务

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

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
  • 明确定义输入文件 - 避免不必要的缓存失效
  • 使用工作区协议 - 如
    "@myorg/ui": "workspace:*"
  • 启用远程缓存 - 在CI和本地环境间共享缓存
  • 在CI中使用过滤 - 仅构建有变更的包
  • 缓存构建输出 - 而非源代码

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
  • 不要缓存开发服务器 - 使用
    persistent: true
    替代
  • 不要在环境变量中包含敏感信息 - 使用运行时环境变量
  • 不要忽略dependsOn - 会导致竞争条件
  • 不要过度过滤 - 可能会遗漏依赖项

Resources

参考资源