11.4 · difficulty 2/4 · 12 min read
Turborepo Task Orchestration
Turborepo makes monorepo builds smart — dependency-aware task ordering, remote caching, and parallel execution. What is the core difference from pnpm -r run build?
Vite 8.1Stable
Why You Need Turborepo
Problems with pnpm -r run build:
- No caching: rebuilds everything every time, even if nothing changed
- Dumb ordering:
-rruns packages in alphabetical directory order, ignoring dependency relationships - No parallelism: executes sequentially by default
Turborepo solves all three:
text
pnpm -r run build:
packages/ui/build ← may run before packages/utils/build (wrong order)
packages/utils/build
apps/web/build
Turborepo (turbo build):
packages/utils/build ← runs first (because ui depends on utils)
packages/ui/build ← runs after utils completes
apps/web/build ← runs after ui completes (parallelizing where possible)
If nothing changed:
All tasks hit the cache and finish instantly!turbo.json Configuration
json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
// "^build" means: before running this task,
// first run build for all packages this package depends on
"dependsOn": ["^build"],
// Output directories: Turborepo caches these
"outputs": ["dist/**", ".next/**", "build/**"],
// Inputs that affect the cache: rebuilds only when these files change
// (omit to use all files as inputs)
"inputs": ["src/**", "package.json", "tsconfig.json"]
},
"typecheck": {
"dependsOn": ["^typecheck"]
// No outputs needed (typecheck produces no artifacts)
},
"lint": {
// lint does not depend on the build output of other packages
"dependsOn": []
},
"dev": {
// dev is not cached and is a persistent task (never exits)
"cache": false,
"persistent": true
// dev only runs for apps/; packages/ do not need to re-run dev
},
"clean": {
"cache": false
}
}
}The dependsOn Relationship
json
"build": {
"dependsOn": ["^build"]
}What ^build means:
- For each package, before running its
build - First run
buildfor every package it depends on
text
apps/web depends on packages/ui, which depends on packages/utils
turbo build apps/web:
1. packages/utils build ← first
2. packages/ui build ← after utils completes
3. apps/web build ← after ui completesIf dependsOn is omitted or set to [], tasks across packages run in parallel.
How Caching Works
Turborepo computes a hash for each task:
text
task hash = hash(
input files (determined by inputs),
output file paths (outputs),
environment variables (env),
task configuration
)When the hash hits the cache:
- Local cache: restores the previous output files instantly
- Remote cache: downloads the previous output from the cloud — shared across the team
Remote Cache
Configure Vercel remote cache:
bash
# Log in to Vercel
npx turbo linkOr self-host a cache server using the turbo-remote-cache package.
Once configured, different developers' local machines share the build cache:
bash
# Developer A runs a build
turbo build # → output cached to remote
# Developer B pulls the code and builds
turbo build # → hits the remote cache, finishes instantlyCommon Commands
bash
# Build all packages
turbo build
# Build only a specific package (and its dependencies)
turbo build --filter @my/web
# Show the task execution graph (dry run, no actual execution)
turbo build --dry-run
# Clear the local cache
turbo clean
# Watch task progress in real time
turbo build --ui streamSelf-check
- What does Turborepo's cache hash depend on? Under what conditions does the cache become invalid?
- What is the difference between
dependsOn: ["^build"]anddependsOn: ["build"]? - Why should the
devtask havecache: falseandpersistent: true? - What benefit does remote caching provide for team collaboration?
json
// Design a turbo.json for the following scenario:
// Tasks:
// - build: generate artifacts
// - test: run unit tests (depends on build)
// - e2e: run end-to-end tests (depends on test)
// - lint: code linting (does not depend on other tasks)
// - docs: generate documentation (depends on build)
//
// Special requirements:
// - When build hits the cache, test should also hit the cache
// - e2e is too slow — do not cache it
// - lint can run in parallel with build
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
// TODO
},
"test": {
// TODO
},
"e2e": {
// TODO
},
"lint": {
// TODO
},
"docs": {
// TODO
}
}
}