Skip to content
Vercel

Turborepo 2.9

Monday, March 30th, 2026
Anthony Shew
Name
Anthony Shew
X
@anthonysheww

Turborepo 2.9 concentrates on quality, including improvements for performance, capability, and simplifying adoption paths:

Additionally, we've closed 141 Issues since Turborepo 2.8, addressing 305 👍 reactions, reducing our total open Issue count by 70%.


Upgrade today by running npx @turbo/codemod migrate or get started with npx create-turbo@latest.

Terminal
# Use the automated upgrade CLI
pnpm dlx @turbo/codemod migrate

# Start a new repository
pnpm dlx create-turbo@latest
Terminal
# Use the automated upgrade CLI
yarn dlx @turbo/codemod migrate

# Start a new repository
yarn dlx create-turbo@latest
Terminal
# Use the automated upgrade CLI
npx @turbo/codemod migrate

# Start a new repository
npx create-turbo@latest
Terminal
# Use the automated upgrade CLI
bunx @turbo/codemod migrate

# Start a new repository
bunx create-turbo@latest

Install or update your Turborepo Agent Skills:

Terminal
# Install the Turborepo Agent Skill
pnpm dlx skills add vercel/turborepo

# Update the Turborepo Agent Skill
pnpm dlx skills update vercel/turborepo
Terminal
# Install the Turborepo Agent Skill
yarn dlx skills add vercel/turborepo

# Update the Turborepo Agent Skill
yarn dlx skills update vercel/turborepo
Terminal
# Install the Turborepo Agent Skill
npx skills add vercel/turborepo

# Update the Turborepo Agent Skill
npx skills update vercel/turborepo
Terminal
# Install the Turborepo Agent Skill
bunx skills add vercel/turborepo

# Update the Turborepo Agent Skill
bunx skills update vercel/turborepo

Time to First Task improved up to 96%

Turborepo's most important job is to schedule your monorepo's tasks to be as fast as possible. Whenever you use turbo run, our first step is to analyze your monorepo's structure and scripts to create a graph of your tasks. The Task Graph describes what tasks depend on each other, so we know how to run and cache the work in your repository.

Once we're done creating your Task Graph, we begin executing your tasks. "Time to First Task" is our name for the time between when you invoke turbo run and when the first script in your monorepo begins executing. In general, Time to First Task represents Turborepo's overhead in your monorepo, and we've made it faster in this release.

Time to First Task (compared to 2.8.0)

Vercel's backend monorepo (1037 packages)

91% faster

Before

8.1s

After

716ms

Vercel's frontend monorepo (132 packages)

81% faster

Before

1.9s

After

361ms

create-turbo (6 packages)

80% faster

Before

676ms

After

132ms

Benchmarked on Apple M4 Max, 36 GB RAM, macOS 15.7.1

The performance boost you see will depend on the size and complexity of your repository - but we expect dramatic improvement for all Turborepos. We've tested these improvements across many open and closed source Turborepos, and the largest performance upgrade we saw was 96%.

For a technical deep dive on how we accomplished this, read our post on the Vercel blog.

turbo query is now stable

turbo query has graduated from Experimental status to Stable. Originally introduced in Turborepo 2.2, turbo query lets you run GraphQL queries against your monorepo's Package and Task Graphs.

Terminal
# Open the GraphiQL playground
turbo query

# Run a query inline
turbo query "{ packages { items { name } } }"

# Run a query from a file
turbo query --file=my-query.gql

This feature lets you quickly answer questions like:

  • "How many packages are in this repository?"
  • "How many of our applications depend on our @repo/ui package?"
  • "Why does changing this file result in cache misses in our web application?"

And much more. We've also seen that coding agents are incredibly adept at writing queries to extract deep insights from Turborepo's knowledge of your repo, especially after putting the GraphQL schema into their context using turbo query --schema.

We're also introducing a few shorthands so you don't have to write verbose GraphQL queries to answer your most common questions.

turbo query affected

The turbo query affected shorthand checks which packages and tasks are affected by code changes in your repository. The command outputs structured JSON data that you can use to build flexible CI pipelines.

Terminal
# Get all tasks affected by source code changes
turbo query affected

# Get `build` tasks affected by source code changes
turbo query affected --tasks build

# Get all packages affected by source code changes
turbo query affected --packages

Additionally, we encourage you to use this primitive to replace your existing usage of turbo-ignore, which is now deprecated. See the migration guide to learn more.

turbo query ls

The turbo query ls shorthand lists packages and some useful details about them. You can also drill into a specific package to see its dependencies and tasks.

Terminal
# List all packages
turbo query ls

# Get details for a specific package
turbo query ls web

# List only affected packages
turbo query ls --affected

# Filter packages using selectors
turbo query ls --filter=./apps/*

By default, this command pretty prints for easier readability. You can also use --output=json for machine-readable data.

Visit the turbo query documentation to learn more.

Easier adoption for large repositories

Circular package dependencies are a common reality in large monorepos. While they come with significant drawbacks, they happen. The example below shows how easily packages can mutually depend on each other.

./packages/ui/package.json
{
  "name": "@repo/ui",
  "dependencies": {
    "@repo/utils": "workspace:*"
  }
}
./packages/utils/package.json
{
  "name": "@repo/utils",
  "dependencies": {
    "@repo/ui": "workspace:*"
  }
}

Previously, Turborepo would exit with an error when it detected a cycle in your package dependency graph. This is because Turborepo needs a Directed Acyclic Graph to operate on - but Turborepo executes the Task Graph, not the Package Graph. That's a subtle mismatch in what Turborepo was validating, and what Turborepo actually needs to work.

Starting with this release, Turborepo handles Package Graph cycles more gracefully. Instead of validating your Package Graph, Turborepo now validates your Task Graph.

In a monorepo with a Package Graph cycle like the example above:

./turbo.json
{
  "tasks": {
    // Works, since there are no task dependencies
    // Task Graph cycles are impossible
    "simple-task": {},
    // Errors, since there's a Task Graph cycle
    "build": { "dependsOn": ["^build"] }
  }
}

In practice, this makes it much easier to incrementally adopt Turborepo on a task-by-task basis. You can slowly build up your Task Graph while unwinding the cycles in your Package Graph at your own pace.

OpenTelemetry (Experimental)

You can now send Turborepo build metrics to any OTLP-compatible observability backend like Grafana, Datadog, or Honeycomb.

Enable it with the experimentalObservability Future Flag in your turbo.json:

turbo.json
{
  "futureFlags": { "experimentalObservability": true },
  "experimentalObservability": {
    "otel": {
      "enabled": true,
      "endpoint": "http://otel-collector.example.com:4317",
      "protocol": "grpc"
    }
  }
}

Turborepo will emit metrics like turbo.run.duration_ms, turbo.run.tasks.cached, and turbo.run.tasks.failed to your collector.

To try it out locally, use:

Terminal
npx create-turbo@latest -e with-otel

Follow the README for a Docker Compose setup with an OTel Collector and pre-configured dashboards.

To learn more about the experimental native OTel collector, visit the documentation.

Structured logging (Experimental)

Turborepo now supports machine-readable JSON output for programmatic consumption.

--json

Stream all output to the terminal as newline-delimited JSON (NDJSON):

Terminal
turbo run build --json

Each line is a JSON object with a consistent schema:

{
  "timestamp": 1710000000000,
  "source": "web#build",
  "level": "stdout",
  "text": "Compiled successfully"
}

--log-file

Write structured logs to a file while keeping normal terminal output:

Terminal
# Log to .turbo/logs/<epoch-millis>.json
turbo run build --log-file

# Or specify a custom path
turbo run build --log-file=./build-log.json

These two flags can also be combined, like turbo run lint --json --log-file=logs.json. Visit the documentation to learn more.

Future Flags and deprecations

We're preparing Turborepo's API surface for 3.0 with Future Flags and deprecations. Future Flags let you opt into 3.0 behaviors now, giving you a gradual migration path instead of a big-bang upgrade. Our goal is for you to be able to upgrade from your 2.9+ codebase to 3.0 with a PR that deletes the Future Flags and increments your version - no functional change.

Future Flags

Each Future Flag can be adopted at any time, individually, and at your own pace.

FlagDescription
globalConfigurationMoves top-level keys into a global key.
affectedUsingTaskInputs--affected selects at task-level granularity using inputs globs instead of selecting all tasks in a changed package.
watchUsingTaskInputsturbo watch re-runs only tasks whose inputs match changed files instead of all tasks in the package.
filterUsingTasks--filter resolves at the task level, using inputs globs for git-range filters and traversing the Task Graph for ... syntax.
pruneIncludesGlobalFilesturbo prune copies files matching globalDependencies globs into the output directory.
errorsOnlyShowHashShows task hashes when using outputLogs: "errors-only" for better visibility into running tasks.
longerSignatureKeyEnforces a minimum 32-byte TURBO_REMOTE_CACHE_SIGNATURE_KEY to prevent weak HMAC signatures.
experimentalObservabilityGates the OpenTelemetry support described above.

Deprecations

The following features are deprecated and will show warnings when used. Each deprecation has a corresponding replacement that you can use ahead of 3.0.

DeprecatedReplacement
turbo-ignoreturbo query affected
turbo scanNone. Functionality is now obsolete.
--parallelpersistent and with in turbo.json
--no-cache--cache=local:r,remote:r
TURBO_REMOTE_ONLY environment variable--cache=remote:rw
--remote-only--cache=remote:rw
TURBO_REMOTE_CACHE_READ_ONLY environment variable--cache=local:rw,remote:r
--remote-cache-read-only--cache=local:rw,remote:r
TURBO_DAEMON environment variableDaemon no longer used
--daemon / --no-daemonDaemon no longer used
daemon in turbo.jsonDaemon no longer used
--graph with .png/.jpg/.pdf.svg, .html, .mermaid, or .dot
--graph with .jsonturbo query
--scope for turbo prunePositional args (e.g. turbo prune web)

All deprecated features will continue to work until a future major version. We recommend migrating at your convenience to prepare for Turborepo 3.0.

All changes

Acknowledgments and community

Turborepo is the result of the combined work of all of its contributors, including our core team: Anthony and Tom.

We also thank everyone who contributed to this release of Turborepo: @04cb, @alexmkio, @antoinelyset, @attehuhtakangas, @bestickley, @bkonkle, @ddmoney420, @EmojiPati, @floriansalihovic, @frankeld, @GabrielBB, @Goldyvaiiii, @haydenbleasel, @i5d6, @Jaredw2289-svg, @kyedavey, @lilianakatrina684-a11y, @marko-hologram, @mehulkar, @molebox, @MrNaif2018, @Netail, @odaysec, @PeterPCW, @prudentbird, @ramanverse, @Rohan5commit, @serjooo, @sleitor, @styfle, and @veeceey.

Thank you for your continued support, feedback, and collaboration to make Turborepo your build tool of choice. To learn how to get involved, visit the Community page.