turborepo-caching
Configure Turborepo for efficient monorepo builds with local and remote caching. Use when setting up Turborepo, optimizing build pipelines, or implementing distributed caching.
Turborepo Caching
Production patterns for Turborepo build optimization.
Do not use this skill when
Instructions
resources/implementation-playbook.md.Use this skill when
Core Concepts
1. Turborepo Architecture
Workspace Root/
├── apps/
│ ├── web/
│ │ └── package.json
│ └── docs/
│ └── package.json
├── packages/
│ ├── ui/
│ │ └── package.json
│ └── config/
│ └── package.json
├── turbo.json
└── package.json2. 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 loginLink to Vercel project
npx turbo linkRun with remote cache
turbo build --remote-onlyCI environment variables
TURBO_TOKEN=your-token
TURBO_TEAM=your-team# .github/workflows/ci.yml
name: CIon:
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/webBuild package and its dependencies
turbo build --filter=@myorg/web...Build package and its dependents
turbo build --filter=...@myorg/uiBuild 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/docsExclude 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-runVerbose output with hashes
turbo build --verbosity=2Show task graph
turbo build --graphForce no cache
turbo build --forceShow cache status
turbo build --summarizeDebug specific task
TURBO_LOG_VERBOSITY=debug turbo build --filter=@myorg/webBest Practices
Do's
"@myorg/ui": "workspace:"Don'ts
persistent: true