Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Configure and optimize Turborepo monorepo build pipelines with correct task structure, caching, and CI setup.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/configuration/gotchas.md
1# Configuration Gotchas23Common mistakes and how to fix them.45## #1 Root Scripts Not Using `turbo run`67Root `package.json` scripts for turbo tasks MUST use `turbo run`, not direct commands.89```json10// WRONG - bypasses turbo, no parallelization or caching11{12"scripts": {13"build": "bun build",14"dev": "bun dev"15}16}1718// CORRECT - delegates to turbo19{20"scripts": {21"build": "turbo run build",22"dev": "turbo run dev"23}24}25```2627**Why this matters:** Running `bun build` or `npm run build` at root bypasses Turborepo entirely - no parallelization, no caching, no dependency graph awareness.2829## #2 Using `&&` to Chain Turbo Tasks3031Don't use `&&` to chain tasks that turbo should orchestrate.3233```json34// WRONG - changeset:publish chains turbo task with non-turbo command35{36"scripts": {37"changeset:publish": "bun build && changeset publish"38}39}4041// CORRECT - use turbo run, let turbo handle dependencies42{43"scripts": {44"changeset:publish": "turbo run build && changeset publish"45}46}47```4849If the second command (`changeset publish`) depends on build outputs, the turbo task should run through turbo to get caching and parallelization benefits.5051## #3 Overly Broad globalDependencies5253`globalDependencies` affects hash for ALL tasks in ALL packages. Be specific.5455```json56// WRONG - affects all hashes57{58"globalDependencies": ["**/.env.*local"]59}6061// CORRECT - move to specific tasks that need it62{63"globalDependencies": [".env"],64"tasks": {65"build": {66"inputs": ["$TURBO_DEFAULT$", ".env*"],67"outputs": ["dist/**"]68}69}70}71```7273**Why this matters:** `**/.env.*local` matches .env files in ALL packages, causing unnecessary cache invalidation. Instead:7475- Use `globalDependencies` only for truly global files (root `.env`)76- Use task-level `inputs` for package-specific .env files with `$TURBO_DEFAULT$` to preserve default behavior7778With `futureFlags.globalConfiguration`, this is less of a concern because `global.inputs` acts as implicit task inputs — tasks can opt out of specific files with negation globs. But keeping the list focused is still good practice.7980## #4 Repetitive Task Configuration8182Look for repeated configuration across tasks that can be collapsed.8384```json85// WRONG - repetitive env and inputs across tasks86{87"tasks": {88"build": {89"env": ["API_URL", "DATABASE_URL"],90"inputs": ["$TURBO_DEFAULT$", ".env*"]91},92"test": {93"env": ["API_URL", "DATABASE_URL"],94"inputs": ["$TURBO_DEFAULT$", ".env*"]95}96}97}9899// BETTER - use globalEnv and globalDependencies100{101"globalEnv": ["API_URL", "DATABASE_URL"],102"globalDependencies": [".env*"],103"tasks": {104"build": {},105"test": {}106}107}108```109110**When to use global vs task-level:**111112- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config113- Task-level `env` / `inputs` - use when only specific tasks need it114115## #5 Using `../` to Traverse Out of Package in `inputs`116117Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead.118119```json120// WRONG - traversing out of package121{122"tasks": {123"build": {124"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]125}126}127}128129// CORRECT - use $TURBO_ROOT$ for repo root130{131"tasks": {132"build": {133"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]134}135}136}137```138139## #6 MOST COMMON MISTAKE: Creating Root Tasks140141**Prefer package tasks over Root Tasks.**142143When you need to create a task (build, lint, test, typecheck, etc.), default to package tasks:1441451. Add the script to **each relevant package's** `package.json`1462. Register the task in root `turbo.json`1473. Root `package.json` only contains `turbo run <task>`148149```json150// WRONG - DO NOT DO THIS151// Root package.json with task logic152{153"scripts": {154"build": "cd apps/web && next build && cd ../api && tsc",155"lint": "eslint apps/ packages/",156"test": "vitest"157}158}159160// CORRECT - DO THIS161// apps/web/package.json162{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }163164// apps/api/package.json165{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }166167// packages/ui/package.json168{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }169170// Root package.json - ONLY delegates171{ "scripts": { "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test" } }172173// turbo.json - register tasks174{175"tasks": {176"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },177"lint": {},178"test": {}179}180}181```182183**Why this matters:**184185- Package tasks run in **parallel** across all packages186- Each package's output is cached **individually**187- You can **filter** to specific packages: `turbo run test --filter=web`188189Root Tasks (`//#taskname`) defeat all these benefits when a task can live in packages. Only use them for tasks that truly cannot exist in any package, such as Vitest Projects' `//#test`, repo-wide release scripts, or tooling that does not invoke `turbo` itself.190191## #7 Tasks That Need Parallel Execution + Cache Invalidation192193Some tasks can run in parallel (don't need built output from dependencies) but must still invalidate cache when dependency source code changes. Using `dependsOn: ["^taskname"]` forces sequential execution. Using no dependencies breaks cache invalidation.194195**Use Transit Nodes for these tasks:**196197```json198// WRONG - forces sequential execution (SLOW)199"my-task": {200"dependsOn": ["^my-task"]201}202203// ALSO WRONG - no dependency awareness (INCORRECT CACHING)204"my-task": {}205206// CORRECT - use Transit Nodes for parallel + correct caching207{208"tasks": {209"transit": { "dependsOn": ["^transit"] },210"my-task": { "dependsOn": ["transit"] }211}212}213```214215**Why Transit Nodes work:**216217- `transit` creates dependency relationships without matching any actual script218- Tasks that depend on `transit` gain dependency awareness219- Since `transit` completes instantly (no script), tasks run in parallel220- Cache correctly invalidates when dependency source code changes221222**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs.223224## Missing outputs for File-Producing Tasks225226**Before flagging missing `outputs`, check what the task actually produces:**2272281. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`)2292. Determine if it writes files to disk or only outputs to stdout2303. Only flag if the task produces files that should be cached231232```json233// WRONG - build produces files but they're not cached234"build": {235"dependsOn": ["^build"]236}237238// CORRECT - outputs are cached239"build": {240"dependsOn": ["^build"],241"outputs": ["dist/**"]242}243```244245No `outputs` key is fine for stdout-only tasks. For file-producing tasks, missing `outputs` means Turbo has nothing to cache.246247## Forgetting ^ in dependsOn248249```json250// WRONG - looks for "build" in SAME package (infinite loop or missing)251"build": {252"dependsOn": ["build"]253}254255// CORRECT - runs dependencies' build first256"build": {257"dependsOn": ["^build"]258}259```260261The `^` means "in dependency packages", not "in this package".262263## Missing persistent on Dev Tasks264265```json266// WRONG - dependent tasks hang waiting for dev to "finish"267"dev": {268"cache": false269}270271// CORRECT272"dev": {273"cache": false,274"persistent": true275}276```277278## Package Config Missing extends279280```json281// WRONG - packages/web/turbo.json282{283"tasks": {284"build": { "outputs": [".next/**"] }285}286}287288// CORRECT289{290"extends": ["//"],291"tasks": {292"build": { "outputs": [".next/**"] }293}294}295```296297Without `"extends": ["//"]`, Package Configurations are invalid.298299## Root Tasks Need Special Syntax300301To run a task defined only in root `package.json`:302303```bash304# WRONG305turbo run format306307# CORRECT308turbo run //#format309```310311And in dependsOn:312313```json314"build": {315"dependsOn": ["//#codegen"] // Root package's codegen316}317```318319## Overwriting Default Inputs320321```json322// WRONG - only watches test files, ignores source changes323"test": {324"inputs": ["tests/**"]325}326327// CORRECT - extends defaults, adds test files328"test": {329"inputs": ["$TURBO_DEFAULT$", "tests/**"]330}331```332333Without `$TURBO_DEFAULT$`, you replace all default file watching.334335## Excluding `global.inputs` Without `$TURBO_DEFAULT$`336337When using `futureFlags.globalConfiguration`, `global.inputs` values are prepended to every task's inputs. If you want to exclude a global input from a specific task, you **must** include `$TURBO_DEFAULT$` to preserve default file hashing.338339```json340// WRONG - task hashes NO files at all (global input cancelled, no defaults)341"build": {342"inputs": ["!$TURBO_ROOT$/config.txt"]343}344345// CORRECT - task hashes all package files, minus config.txt346"build": {347"inputs": ["$TURBO_DEFAULT$", "!$TURBO_ROOT$/config.txt"]348}349```350351Without `$TURBO_DEFAULT$`, the only inclusion glob comes from `global.inputs`, which the negation cancels out. The task ends up with no inclusions and no default file hashing, so it hashes nothing. Changes to source files won't cause cache misses.352353## Caching Tasks with Side Effects354355```json356// WRONG - deploy might be skipped on cache hit357"deploy": {358"dependsOn": ["build"]359}360361// CORRECT362"deploy": {363"dependsOn": ["build"],364"cache": false365}366```367368Always disable cache for deploy, publish, or mutation tasks.369