Back to monorepo orchestration Target affected workspaces Configure turbo pipelines Compare the Nx approach

Turborepo Pipeline Configuration

Core turbo.json Schema & Initialization

Initialize the pipeline definition at the repository root. Enforce strict JSON schema validation to enable IDE autocompletion and catch structural drift early.

// turbo.json
{
 "$schema": "https://turbo.build/schema.json",
 "globalDependencies": [".env"],
 "pipeline": {
 "build": {
 "dependsOn": ["^build"],
 "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
 "env": ["NODE_ENV", "CI"]
 },
 "lint": {
 "dependsOn": [],
 "outputs": []
 },
 "test": {
 "dependsOn": ["build"],
 "inputs": ["src/**/*.ts", "test/**/*.ts"]
 }
 }
}

Key Directives:

  • $schema: Binds to the official Turborepo schema. Required for v1.10+ and v2.x compatibility.
  • pipeline: Defines the execution DAG. (Note: v2.x aliases pipeline to tasks; both are supported during migration.)
  • globalDependencies: Files that invalidate the entire cache when modified. Use sparingly.

Aligns with foundational Monorepo Architecture & Orchestration principles for strict task isolation and deterministic execution boundaries.


Task Dependency Graphs (dependsOn)

Turborepo requires explicit dependency declarations. Unlike Nx Workspace Architecture which infers graphs via project.json targets, Turborepo relies on dependsOn arrays to construct topological execution orders.

Syntax Behavior Use Case
"^build" Executes build in all upstream workspace dependencies first Cross-package compilation chains
"build" Executes build in the current workspace only Self-contained tasks
"$TURBO_DEFAULT$" Inherits default task configuration Reducing boilerplate in large repos

Terminal Verification:

# Dry-run to visualize execution order without running tasks
turbo run build --dry=json | jq '.tasks[].taskId'

# Detect circular dependencies before CI
turbo run build --graph=dependency-graph.dot

Anti-Pattern: Omitting ^ on shared library builds forces downstream packages to run before dependencies compile, causing MODULE_NOT_FOUND errors or stale type definitions.


Cache Hashing & Deterministic Outputs

Cache boundaries are defined by inputs (what triggers a rebuild) and outputs (what gets cached). Misconfigured globs cause cache bloat or perpetual cache misses.

# Scope execution to affected packages only (requires pnpm/npm/yarn workspace root)
pnpm exec turbo run build --filter="...[HEAD^1]"

# Inspect cache hit/miss metrics
turbo run build --log-order=stream --output-logs=hash-only

Glob Configuration Rules:

  • outputs: Must capture only deterministic artifacts. Always exclude volatile directories (.next/cache, node_modules, .turbo).
  • inputs: Restrict to source files. Avoid **/* which hashes lockfiles, configs, and CI metadata unnecessarily.
  • Integration with pnpm Workspace Filtering enables precise cache invalidation, reducing CI compute costs by 40-70% on incremental PRs.

Environment Variable Security & Passthrough

Turborepo does not inherit host shell variables by default. All required variables must be explicitly declared to ensure cache key stability and prevent secret leakage.

Field Scope Cache Impact Security Posture
env Task-level Changes only invalidate the specific task cache Recommended for API keys, feature flags
globalEnv Repository-wide Changes invalidate all cached tasks Use only for compiler flags (e.g., CC, CXX)
passThroughEnv Task-level Passes host vars without hashing Never use for secrets; breaks determinism

Security Enforcement:

{
 "pipeline": {
 "deploy": {
 "env": ["AWS_REGION", "DEPLOY_ENV"],
 "outputs": []
 }
 }
}
  • Never declare TURBO_TOKEN, NPM_TOKEN, or GITHUB_TOKEN in globalEnv. Scope them to deployment tasks only.
  • Strict environment scoping correlates directly with cache hit rate stability, as documented in Nx vs Turborepo: Performance Benchmarks.

CI/CD Pipeline Integration & Remote Caching

Deploy pipelines with explicit concurrency limits, remote cache authentication, and targeted execution flags.

GitHub Actions Snippet

- name: Setup Turborepo Remote Cache
 run: |
 echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
 echo "TURBO_TEAM=${{ vars.TURBO_TEAM }}" >> $GITHUB_ENV

- name: Build & Test
 run: |
 pnpm exec turbo run build test \
 --cache-dir=.turbo/cache \
 --concurrency=4 \
 --filter="...[origin/main]"

Production Flags & Policies

Flag Purpose CI Recommendation
--force Bypasses cache, forces execution Debugging only. Never in mainline CI.
--filter Targets workspaces Use ...[HEAD^1] for PRs, ...[origin/main] for main.
--concurrency Limits parallel workers Set to $(nproc) or CI runner cores. Prevents OOM.
--cache-dir Local cache path Mount as CI volume for job-level persistence.

Cache Retention Strategy:

  • Configure remote cache eviction via Turborepo Cloud dashboard (default: 30 days).
  • Implement turbo prune --docker for multi-stage builds to minimize image layers.
  • Use --output-logs=errors-only in CI to reduce log ingestion costs.

Common Mistakes & Remediation

Mistake Impact Fix
Omitting outputs arrays 0% cache hit rate; artifacts regenerated on every run Explicitly declare dist/**, build/**, etc.
Using globalEnv for credentials Secrets exposed to all tasks; full cache invalidation on rotation Move to task-level env arrays.
Implicit shell env inheritance Non-deterministic builds across CI runners Declare all required vars in turbo.json.
Incorrect ^ syntax in dependsOn Circular dependency locks or skipped upstream builds Use "^task" for workspace deps, "task" for local.
Caching volatile dirs (.next/cache, node_modules) Remote cache payload bloat; degraded network performance Prefix with ! in outputs (e.g., "!.next/cache/**").

FAQ

How do I prevent Turborepo from caching non-deterministic outputs like timestamps? Exclude volatile files from the outputs array and restrict inputs to source code only. Configure turbo.json to ignore .turbo and framework-specific cache directories. Use --force exclusively for debugging; never in production pipelines.

What is the difference between env and globalEnv in turbo.json? env scopes environment variables to a specific task, ensuring cache keys only change when those variables change. globalEnv applies variables to all tasks, invalidating the entire cache if any listed variable changes. Use env for task-specific configuration and reserve globalEnv for truly global compiler flags.

How does Turborepo handle cross-package dependencies during pipeline execution? Turborepo uses the ^ prefix in dependsOn (e.g., "^build") to automatically resolve and execute dependent tasks in upstream workspaces before running the current task. This ensures strict topological ordering without manual script chaining or && operators.

Can I use Turborepo with non-JavaScript toolchains? Yes. Turborepo is language-agnostic and operates on filesystem outputs and environment variables. Configure outputs and inputs to match your toolchain's artifact structure (e.g., target/ for Rust, bin/ for Go) and ensure deterministic execution by excluding temp directories.