Fix: Turborepo Not Working — Cache Never Hits, Pipeline Not Running, or Workspace Task Fails
Quick Answer
How to fix Turborepo issues — turbo.json pipeline configuration, cache keys, remote caching setup, workspace filtering, and common monorepo task ordering mistakes.
The Problem
Turborepo never uses the cache — every build reruns from scratch:
turbo run build
# cache miss, executing ← every single timeOr a task fails because it runs before its dependency:
turbo run test
# Error: Cannot find module '@my-app/utils'
# (utils package hasn't been built yet)Or turbo run build --filter=web builds more (or fewer) packages than expected:
turbo run build --filter=web
# Also builds unrelated packages — or misses necessary dependenciesOr remote caching isn’t working in CI despite being configured:
# CI logs show: "Remote caching disabled"Why This Happens
Turborepo’s behavior depends heavily on correct turbo.json configuration:
- Cache keys include all inputs — if the cache always misses, either the inputs are constantly changing (e.g., a timestamp in a generated file), or the
inputsconfiguration isn’t narrow enough to exclude irrelevant files. - Task dependencies must be declared —
"dependsOn": ["^build"]means “run thebuildtask of all workspace dependencies first.” Without this, tasks run in parallel without waiting for dependencies. --filteruses package names, not directory paths —--filter=weblooks for a package namedwebinpackage.json, not theweb/directory. If your package is named@my-app/web, use--filter=@my-app/web.- Remote caching requires a valid token — without
TURBO_TOKENandTURBO_TEAMenvironment variables (or Vercel authentication), remote cache falls back to local-only.
Fix 1: Configure turbo.json Correctly
The turbo.json pipeline defines task ordering, caching, and outputs:
// turbo.json — at the monorepo root
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env"], // Cache invalidated when .env changes
"tasks": {
// Build task — depends on dependencies' builds first
"build": {
"dependsOn": ["^build"], // ^ means "dependency packages' build"
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"inputs": ["src/**", "package.json", "tsconfig.json"]
},
// Test — depends on this package's own build
"test": {
"dependsOn": ["build"], // No ^ = same package's build
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "jest.config.*"]
},
// Lint — no task dependencies, fully parallel
"lint": {
"outputs": [], // No outputs to cache
"inputs": ["src/**", ".eslintrc.*", ".eslintignore"]
},
// Dev server — never cache, always run
"dev": {
"cache": false,
"persistent": true // Long-running task — turbo won't wait for it to finish
},
// Type check
"typecheck": {
"dependsOn": ["^build"],
"outputs": []
}
}
}Common dependsOn patterns:
{
"tasks": {
"build": {
"dependsOn": ["^build"]
// ^ = all dependency packages must finish their "build" first
},
"test": {
"dependsOn": ["build"]
// No ^ = this package's own "build" must finish first
},
"deploy": {
"dependsOn": ["build", "test", "^deploy"]
// Own build+test done, AND dependency packages' deploy done
}
}
}Fix 2: Fix Cache Always Missing
Diagnose why the cache never hits:
# Run with verbose output to see cache key details
turbo run build --verbosity=2
# Dry run — shows what would run without executing
turbo run build --dry=json | jq '.tasks[] | {task: .task, package: .package, cache: .cache}'
# Force a cache miss to regenerate
turbo run build --force
# Check what's included in the cache hash
TURBO_LOG_ORDER=stream turbo run build 2>&1 | grep "hash"Common cache miss causes:
// PROBLEM 1: outputs not specified — nothing to cache
{
"tasks": {
"build": {
"dependsOn": ["^build"]
// Missing "outputs" — cache always misses because nothing is saved
}
}
}
// FIX: specify what the build produces
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"] // These files are cached and restored
}
}
}
// PROBLEM 2: inputs too broad — every file change invalidates cache
{
"tasks": {
"test": {
"outputs": ["coverage/**"]
// No "inputs" = ALL files are inputs — README.md change busts the cache
}
}
}
// FIX: narrow the inputs
{
"tasks": {
"test": {
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "*.config.*", "package.json"]
}
}
}Exclude generated files from cache inputs:
{
"tasks": {
"build": {
"inputs": [
"src/**",
"public/**",
"!src/**/__generated__/**", // Exclude generated files
"!src/**/*.stories.tsx", // Exclude storybook files
"package.json",
"tsconfig.json"
],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
}
}
}Fix 3: Filter Tasks to Specific Packages
Use --filter to run tasks for specific packages and their dependencies:
# Run by package name (from package.json "name" field)
turbo run build --filter=@my-app/web
# Multiple packages
turbo run build --filter=@my-app/web --filter=@my-app/api
# By directory (use ./ prefix)
turbo run build --filter=./apps/web
# Include all dependencies of 'web' (packages web depends on)
turbo run build --filter=@my-app/web...
# Include all dependents of 'utils' (packages that depend on utils)
turbo run build --filter=...@my-app/utils
# Run only on changed packages (compared to main branch)
turbo run test --filter=[main]
# Run only on packages changed since last commit
turbo run test --filter=[HEAD^1]Package naming — always check package.json:
// apps/web/package.json
{
"name": "@my-app/web", // This is the filter name, not the directory
"scripts": {
"build": "next build"
}
}
// WRONG — directory name without @scope
turbo run build --filter=web
// CORRECT — full package name from package.json
turbo run build --filter=@my-app/webFix 4: Set Up Remote Caching
Remote caching shares the build cache across CI runs and team members:
# Option 1: Vercel Remote Cache (free, official)
# Login to Vercel
npx turbo login
# Link the repo to a Vercel team
npx turbo link
# Now CI builds automatically use the remote cache
# Set these in CI environment:
# TURBO_TOKEN — from Vercel dashboard → Settings → Tokens
# TURBO_TEAM — your Vercel team slug# GitHub Actions — pass Turborepo remote cache credentials
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # Required for --filter=[HEAD^1]
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx turbo run build test lint --filter=[HEAD^1]Self-hosted remote cache (Ducktape/Turborepo Remote Cache):
# Run a self-hosted cache server
docker run -p 3000:3000 \
-e STORAGE_PROVIDER=local \
-e STORAGE_PATH=/data \
-e TURBO_TOKEN=my-secret-token \
ducktape/turborepo-remote-cache
# Point turbo at the custom server
# turbo.json
{
"remoteCache": {
"apiUrl": "http://your-cache-server:3000"
}
}
# Or via environment variable
TURBO_API=http://your-cache-server:3000 turbo run buildFix 5: Workspace Setup
Turborepo works on top of your package manager’s workspaces:
// Root package.json
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint"
},
"devDependencies": {
"turbo": "^2.0.0"
}
}my-monorepo/
├── turbo.json
├── package.json
├── apps/
│ ├── web/
│ │ └── package.json {"name": "@my-app/web"}
│ └── api/
│ └── package.json {"name": "@my-app/api"}
└── packages/
├── ui/
│ └── package.json {"name": "@my-app/ui"}
└── utils/
└── package.json {"name": "@my-app/utils"}Cross-package dependencies:
// apps/web/package.json
{
"name": "@my-app/web",
"dependencies": {
"@my-app/ui": "*", // Reference by workspace path
"@my-app/utils": "*"
}
}
// packages/ui/package.json
{
"name": "@my-app/ui",
"main": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format esm --dts"
}
}Fix 6: Debug Common Errors
“Package not found” in dependent package:
# Ensure the dependency is built before the dependent
# turbo.json must have:
# "build": { "dependsOn": ["^build"] }
# Verify workspace linking
npm ls @my-app/utils # or pnpm ls / bun pm ls
# Check that the built output exists
ls packages/utils/dist/Task hangs indefinitely:
# Add timeout to prevent CI hangs
turbo run build --concurrency=4 # Limit parallel tasks
# Check for circular dependencies
turbo run build --graph # Generates a dependency graph (requires graphviz)
turbo run build --graph=graph.pngpnpm workspace issues:
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'// turbo.json — specify package manager for pnpm
{
"$schema": "https://turbo.build/schema.json",
"tasks": { }
}
// No special config needed — turbo auto-detects pnpm from lockfile# pnpm + turbo — install dependencies
pnpm install
# Run turbo via pnpm
pnpm turbo run build
# or add to root package.json scripts and run: pnpm buildStill Not Working?
Cache hits locally but misses in CI — the most common cause is non-deterministic inputs. Check for files that contain timestamps, random values, or absolute paths that differ between machines. Use turbo run build --dry=json in both environments and compare the hashOfExternalDependencies and inputs fields.
--filter=[HEAD^1] always runs all packages — this requires fetch-depth: 2 (or greater) in actions/checkout. With fetch-depth: 1 (shallow clone), there’s no parent commit to compare against, so Turbo conservatively runs everything. Set fetch-depth: 0 to get full history if you need accurate change detection across more than one commit.
Outputs not restored from cache — if turbo run build reports a cache hit but the dist/ folder is empty, the outputs pattern in turbo.json doesn’t match what the build actually produces. Run the build once, then ls dist/ to see the actual structure, and update the outputs glob accordingly.
For related monorepo issues, see Fix: npm ERR Peer Dep Conflict and Fix: TypeScript Path Alias Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: esbuild Not Working — Plugin Errors, CSS Not Processed, or Output Missing After Build
How to fix esbuild issues — entry points, plugin API, JSX configuration, CSS modules, watch mode, metafile analysis, external packages, and common migration problems from webpack.
Fix: Git Hooks Not Running — Husky Not Working, pre-commit Skipped, or lint-staged Failing
How to fix Git hooks not executing — Husky v9 setup, hook file permissions, lint-staged configuration, pre-commit Python tool, lefthook, and bypassing hooks in CI.
Fix: Docker Multi-Platform Build Not Working — buildx Fails, Wrong Architecture, or QEMU Error
How to fix Docker multi-platform build issues — buildx setup, QEMU registration, --platform flag usage, architecture-specific dependencies, and pushing multi-arch manifests to a registry.
Fix: GitHub Actions Artifacts Not Working — Upload Fails, Download Empty, or Artifact Not Found
How to fix GitHub Actions artifact issues — upload-artifact path patterns, download-artifact across jobs, retention days, artifact name conflicts, and the v3 to v4 migration.