---
title: Configuring turbo.json
description: Learn how to configure Turborepo through `turbo.json`.
product: turborepo
type: reference
summary: Complete reference for all turbo.json configuration options and their behavior.
related:
  - /docs/reference/package-configurations
  - /docs/crafting-your-repository/configuring-tasks
  - /docs/reference/system-environment-variables
---

# Configuring turbo.json

Configure the behavior of `turbo` by using a `turbo.json` file in your Workspace's root directory. You can also:

* Use [Package Configurations](/docs/reference/package-configurations) for more granular control.
* Use `turbo.jsonc` to add comments to your configuration with IDE support.

Global options [#global-options]

extends [#extends]

```jsonc title="./apps/web/turbo.json"
{
  "extends": ["//"],
}
```

Extend from the root `turbo.json` to create specific configuration for a package using [Package Configurations](/docs/reference/package-configurations).

* The `extends` array must start with `["//"]` to inherit configuration from the root `turbo.json`.
* You can also extend from other packages (e.g., `["//", "shared-config"]`).
* If `extends` is used in the root `turbo.json`, it will be ignored.

globalDependencies [#globaldependencies]

```jsonc title="./turbo.json"
{
  "globalDependencies": [".env", "tsconfig.json"],
}
```

A list of globs that you want to include in all task hashes. **If any file matching these globs changes, all tasks will miss cache.** Globs are relative to the location of `turbo.json`.

By default, only the root package.json and lockfile are included in [the global hash](/docs/crafting-your-repository/caching) and can't be ignored. Any added `globalDependencies` will also be included in the global hash.

<Callout type="error">
  Globs must be in the repository's source control root. Globs outside of the
  repository aren't supported.
</Callout>

globalEnv [#globalenv]

```jsonc title="./turbo.json"
{
  "globalEnv": ["GITHUB_TOKEN", "PACKAGE_VERSION", "NODE_ENV"],
}
```

A list of environment variables that you want to impact the hash of all tasks. Any change to these environment variables will cause all tasks to miss cache.

For more on wildcard and negation syntax, [see the `env` section](#env).

globalPassThroughEnv [#globalpassthroughenv]

```jsonc title="./turbo.json"
{
  "globalPassThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"],
}
```

A list of environment variables that you want to make available to tasks. Using this key opts all tasks into [Strict Environment Variable Mode](/docs/crafting-your-repository/using-environment-variables#strict-mode).

Additionally, Turborepo has a built-in set of global passthrough variables for common cases, like operating system environment variables. This includes variables like `HOME`, `PATH`, `APPDATA`, `SHELL`, `PWD`, and more. The full list can be found [in the source code](https://github.com/vercel/turborepo/blob/main/crates/turborepo-lib/src/task_hash.rs).

<Callout type="warn" title="Passthrough values do not contribute to hashes for caching">
  If you want changes in these variables to cause cache misses, you will need to
  include them in [`env`](#env) or [`globalEnv`](#globalenv).
</Callout>

ui [#ui]

Default: `"stream"`

Select a terminal UI for the repository.

`"tui"` allows for viewing each log at once and interacting with the task. `"stream"` outputs logs as they come in and is not interactive.

```json title="./turbo.json"
{
  "ui": "tui" | "stream"
}
```

noUpdateNotifier [#noupdatenotifier]

Default: `false`

When set to `true`, disables the update notification that appears when a new version of `turbo` is available.

```json title="./turbo.json"
{
  "noUpdateNotifier": true
}
```

concurrency [#concurrency]

Default: `"10"`

Set/limit the maximum concurrency for task execution. Must be an integer greater than or equal to `1` or a percentage value like `50%`.

* Use `1` to force serial execution (one task at a time).
* Use `100%` to use all available logical processors.
* This option is ignored if the [`--parallel`](/docs/reference/run#--parallel) flag is also passed.

```jsonc title="./turbo.json"
{
  "concurrency": "1",
}
```

dangerouslyDisablePackageManagerCheck [#dangerouslydisablepackagemanagercheck]

Default: `false`

Turborepo uses your repository's lockfile to determine caching behavior, [Package Graphs](https://turborepo.dev/docs/core-concepts/internal-packages), and more. Because of this, we use [the `packageManager` field](https://nodejs.org/api/packages.html#packagemanager) to help you stabilize your Turborepo.

To help with incremental migration or in situations where you can't use the `packageManager` field, you may use `--dangerously-disable-package-manager-check` to opt out of this check and assume the risks of unstable lockfiles producing unpredictable behavior. When disabled, Turborepo will attempt a best-effort discovery of the intended package manager meant for the repository.

```jsonc title="./turbo.json"
{
  "dangerouslyDisablePackageManagerCheck": true,
}
```

<Callout type="info">
  You may also opt out of this check via
  [`flag`](/docs/reference/run#--dangerously-disable-package-manager-check) or
  the
  [`TURBO_DANGEROUSLY_DISABLE_PACKAGE_MANAGER_CHECK`](https://turborepo.dev/docs/reference/system-environment-variables)
  environment variable.
</Callout>

cacheDir [#cachedir]

Default: `".turbo/cache"`

Specify the filesystem cache directory.

```jsonc title="./turbo.json"
{
  "cacheDir": ".turbo/cache",
}
```

<Callout type="info">
  When no `cacheDir` is specified and you're working in a [Git
  worktree](https://git-scm.com/docs/git-worktree), Turborepo automatically
  shares the cache with the main worktree. This allows cache hits across
  different branches checked out in separate worktrees. Setting an explicit
  `cacheDir` disables this behavior.
</Callout>

daemon [#daemon]

**Deprecated**: The daemon is no longer used for `turbo run` and this option will be removed in version 3.0. The `--daemon` and `--no-daemon` flags are also deprecated.

The daemon is still used by `turbo watch` and the Turborepo LSP.

envMode [#envmode]

Default: `"strict"`

Turborepo's Environment Modes allow you to control which environment variables are available to a task at runtime:

* `"strict"`: Filter environment variables to only those that are specified in the `env` and `globalEnv` keys in `turbo.json`.
* `"loose"`: Allow all environment variables for the process to be available.

```jsonc title="./turbo.json"
{
  "envMode": "strict",
}
```

Read more about [Environment Modes](/docs/crafting-your-repository/using-environment-variables#environment-modes).

futureFlags [#futureflags]

```jsonc title="./turbo.json"
{
  "futureFlags": {
    "errorsOnlyShowHash": true,
  },
}
```

Enable experimental features that will become the default behavior in future versions of Turborepo.

<Callout type="info">
  `futureFlags` can only be set in the root `turbo.json`. An error will be
  thrown if set in a [Package
  Configuration](/docs/reference/package-configurations).
</Callout>

errorsOnlyShowHash [#errorsonlyshowhash]

Default: `false`

When using [`outputLogs: "errors-only"`](#outputlogs), show task hashes when tasks start and complete successfully. This provides visibility into which tasks are running without showing full output logs.

With this flag enabled, successful tasks will show messages like:

* `cache miss, executing <hash> (only logging errors)` - when a task starts execution
* `cache hit, replaying logs (no errors) <hash>` - when a task is restored from cache

```jsonc title="./turbo.json"
{
  "futureFlags": {
    "errorsOnlyShowHash": true,
  },
  "tasks": {
    "build": {
      "outputLogs": "errors-only",
    },
  },
}
```

tags Experimental [#tags-experimental]

```jsonc title="./apps/web/turbo.json"
{
  "tags": ["utils"],
}
```

Adds a tag to a package for use with [Boundaries](/docs/reference/boundaries).

This key only works in [Package Configurations](/docs/reference/package-configurations). Using this key in a root `turbo.json` will result in an error.

Defining tasks [#defining-tasks]

tasks [#tasks]

Each key in the `tasks` object is the name of a task that can be executed by [`turbo run`](/docs/reference/run). Turborepo will search the packages described in your [Workspace's configuration](/docs/crafting-your-repository/structuring-a-repository#specifying-packages-in-a-monorepo) for scripts in `package.json` with the name of the task.

Using the rest of the configuration described in the task, Turborepo will run the scripts in the described order, caching logs and file outputs in [the `outputs` key](#outputs) when provided.

In the example below, we've defined three tasks under the `tasks` key: `build`, `test`, and `dev`.

```jsonc title="./turbo.json"
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
    },
    "test": {
      "outputs": ["coverage/**"],
      "dependsOn": ["build"],
    },
    "dev": {
      "cache": false,
      "persistent": true,
    },
  },
}
```

Task options [#task-options]

Using the options available in the tasks you define in `tasks`, you can describe how `turbo` will run your tasks.

extends (task-level) [#extends-task-level]

Controls whether a task inherits configuration from the extends chain. This option is only available in [Package Configurations](/docs/reference/package-configurations), not in the root `turbo.json`.

```jsonc title="./packages/ui/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "lint": {
      "extends": false, // Exclude this task from the package
    },
  },
}
```

| Value            | Behavior                                                                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `false`          | Task is excluded from inheritance. If no other config is provided, the task won't exist for this package. If other config is provided, creates a fresh task definition with no inheritance. |
| `true` (default) | Task inherits configuration normally from the extends chain.                                                                                                                                |

See [Excluding tasks from inheritance](/docs/reference/package-configurations#excluding-tasks-from-inheritance) for examples and more details.

description [#description]

A human- or agent-readable description of what a task does.

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "description": "Compiles the application for production deployment",
    },
  },
}
```

This field is for documentation purposes only and does not affect task execution or caching behavior.

dependsOn [#dependson]

A list of tasks that are required to complete before the task begins running.

There are three types of `dependsOn` relationships: [dependency relationships](#dependency-relationships), [same-package relationships](#same-package-relationships), and [arbitrary task relationships](#arbitrary-task-relationships).

Dependency relationships [#dependency-relationships]

Prefixing a string in `dependsOn` with a `^` tells `turbo` that the task must wait for tasks in the package's dependencies to complete first. For example, in the `turbo.json` below:

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
    },
  },
}
```

`turbo` starts at the "bottom" of the package graph and recursively visits each package until it finds a package with no internal dependencies. It will then run the `build` task at the end of the dependency chain first, working its way back to the "top" until all `build` tasks are completed in order.

Same package relationships [#same-package-relationships]

Task names without the `^` prefix describe a task that depends on a different task within the same package. For example, in the `turbo.json` below:

```jsonc title="./turbo.json"
{
  "tasks": {
    "test": {
      "dependsOn": ["lint", "build"],
    },
  },
}
```

The `test` task will only run after the `lint` and `build` tasks have completed **in the same package**.

Arbitrary task relationships [#arbitrary-task-relationships]

Specify a task dependency between specific package tasks.

```json title="./turbo.json"
{
  "tasks": {
    "web#lint": {
      "dependsOn": ["utils#build"]
    }
  }
}
```

In this `turbo.json`, the `web#lint` task will wait for the `utils#build` task to complete.

env [#env]

The list of environment variables a task depends on.

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "env": ["DATABASE_URL"], // Impacts hash of all build tasks
    },
    "web#build": {
      "env": ["API_SERVICE_KEY"], // Impacts hash of web's build task
    },
  },
}
```

<Callout type="info">
  Turborepo automatically includes environment variables prefixed by common
  frameworks through [Framework
  Inference](/docs/crafting-your-repository/using-environment-variables#framework-inference).
  For example, if your package is a Next.js project, you do not need to specify
  any environment variables that [start with
  `NEXT_PUBLIC_`](https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser).
</Callout>

Wildcards [#wildcards]

Turborepo supports wildcards for environment variables so you can easily account for all environment variables with a given prefix. For example, the `turbo.json` below include all environment variables that start with `MY_API_` into the hash:

```json title="./turbo.json"
{
  "tasks": {
    "build": {
      "env": ["MY_API_*"]
    }
  }
}
```

Negation [#negation]

A leading `!` means that the entire pattern will be negated. For instance, the `turbo.json` below will ignore the `MY_API_URL` variable.

```json title="./turbo.json"
{
  "tasks": {
    "build": {
      "env": ["!MY_API_URL"]
    }
  }
}
```

Examples [#examples]

| Pattern    | Description                                                                    |
| ---------- | ------------------------------------------------------------------------------ |
| `"*"`      | Matches every environment variable.                                            |
| `"!*"`     | Excludes every environment variable.                                           |
| `"FOO*"`   | Matches `FOO`, `FOOD`, `FOO_FIGHTERS`, etc.                                    |
| `"FOO\*"`  | Resolves to `"FOO*"` and matches `FOO`, `FOOD`, and `FOO_FIGHTERS`.            |
| `"FOO\\*"` | Matches a single environment variable named `FOO*`.                            |
| `"!FOO*"`  | Excludes all environment variables that start with `FOO`.                      |
| `"\!FOO"`  | Resolves to `"!FOO"`, and excludes a single environment variable named `!FOO`. |
| `"\\!FOO"` | Matches a single environment variable named `!FOO`.                            |
| `"FOO!"`   | Matches a single environment variable named `FOO!`.                            |

passThroughEnv [#passthroughenv]

An allowlist of environment variables that should be made available to this task's runtime, even when in [Strict Environment Mode](/docs/crafting-your-repository/using-environment-variables#strict-mode).

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      // Values will be available within `build` scripts
      "passThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"],
    },
  },
}
```

<Callout type="warn">
  Values provided in `passThroughEnv` do not contribute to the cache key for the
  task. If you'd like changes to these variables to cause cache misses, you will
  need to include them in [`env`](#env) or [`globalEnv`](#globalenv).
</Callout>

outputs [#outputs]

A list of file glob patterns relative to the package's `package.json` to cache when the task is successfully completed.

See [`$TURBO_ROOT$`](#turbo_root) if output paths need to be relative to the repository root.

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      // Cache all files emitted to the packages's `dist` directory
      "outputs": ["dist/**"],
    },
  },
}
```

Omitting this key or passing an empty array tells `turbo` to cache nothing (except logs, which are always cached when caching is enabled).

cache [#cache]

Default: `true`

Defines if task outputs should be cached. Setting `cache` to false is useful for long-running development tasks and ensuring that a task always runs when it is in the task's execution graph.

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "outputs": [".svelte-kit/**", "dist/**"], // File outputs will be cached
    },
    "dev": {
      "cache": false, // No outputs will be cached
      "persistent": true,
    },
  },
}
```

inputs [#inputs]

Default: `[]`, all files in the package that are checked into source control

A list of file glob patterns relative to the package's `package.json` to consider when determining if a package has changed. The following files are **always** considered inputs, even if you try to explicitly ignore them:

* `package.json`
* `turbo.json`
* Package manager lockfiles

Visit the [file glob specification](/docs/reference/globs) for more information on globbing syntax.

```jsonc title="./turbo.json"
{
  "tasks": {
    "test": {
      "inputs": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts"],
    },
  },
}
```

<Callout type="warn">
  Using the `inputs` key opts you out of `turbo`'s default behavior of
  considering `.gitignore`. You must reconstruct the globs from `.gitignore` as
  desired or use `$TURBO_DEFAULT$` to build off of the default behavior.
</Callout>

$TURBO_DEFAULT$ [#turbo_default]

Because specifying an `inputs` key immediately opts out of the default behavior, you may use
the special string `$TURBO_DEFAULT$` within the `inputs` array to restore `turbo`'s default behavior. This allows you to tweak the default behavior for more granularity.

```jsonc title="./turbo.json"
{
  "tasks": {
    "check-types": {
      // Consider all default inputs except the package's README
      "inputs": ["$TURBO_DEFAULT$", "!README.md"],
    },
  },
}
```

$TURBO_ROOT$ [#turbo_root]

Tasks might reference a file that lies outside of their directory.

Starting a file glob with `$TURBO_ROOT$` will change the glob to be relative to the root of the repository instead of the package directory.

```jsonc title="./turbo.json"
{
  "tasks": {
    "check-types": {
      // Consider all Typescript files in `src/` and the root tsconfig.json as inputs
      "inputs": ["$TURBO_ROOT$/tsconfig.json", "src/**/*.ts"],
    },
  },
}
```

$TURBO_EXTENDS$ [#turbo_extends]

When using [Package Configurations](/docs/reference/package-configurations), array fields completely replace the values from the root `turbo.json` by default. The `$TURBO_EXTENDS$` microsyntax changes this behavior to **append** instead of **replace**.

This microsyntax can be used in the following array fields:

* `dependsOn`
* `env`
* `inputs`
* `outputs`
* `passThroughEnv`
* `with`

For example, if your root `turbo.json` defines:

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "outputs": ["dist/**"],
    },
  },
}
```

A Package Configuration can add additional outputs while keeping the root outputs:

```jsonc title="./apps/web/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "build": {
      // Inherits "dist/**" from root, and adds ".next/**"
      "outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"],
    },
  },
}
```

Without `$TURBO_EXTENDS$`, the `outputs` array would be completely replaced with `[".next/**", "!.next/cache/**"]`, dropping the `"dist/**"` from the root configuration.

outputLogs [#outputlogs]

Default: `full`

Set output logging verbosity. Can be overridden by the [`--output-logs`](/docs/reference/run#--output-logs-option) CLI option.

| Option        | Description                       |
| ------------- | --------------------------------- |
| `full`        | Displays all logs                 |
| `hash-only`   | Only show the hashes of the tasks |
| `new-only`    | Only show logs from cache misses  |
| `errors-only` | Only show logs from task failures |
| `none`        | Hides all task logs               |

```jsonc title="./turbo.json"
{
  "tasks": {
    "build": {
      "outputLogs": "new-only",
    },
  },
}
```

<Callout type="info">
  When using `errors-only`, you can enable the
  [`errorsOnlyShowHash`](#errorsonlyshowhash) future flag to show task hashes
  when tasks complete successfully, providing visibility into running tasks
  without full output logs.
</Callout>

persistent [#persistent]

Default: `false`

Label a task as `persistent` to prevent other tasks from depending on long-running processes. Persistent tasks are made [interactive](#interactive) by default.

Because a long-running process won't exit, tasks that would depend on it would never run. Once you've labeled the task as persistent, `turbo` will throw an error if other tasks depend on it.

This option is most useful for development servers or other "watch" tasks.

```jsonc title="./turbo.json"
{
  "tasks": {
    "dev": {
      "persistent": true,
    },
  },
}
```

Tasks marked with `persistent` are also `interactive` by default.

interactive [#interactive]

Default: `false` (Defaults to `true` for tasks marked as `persistent`)

Label a task as `interactive` to make it accept inputs from `stdin` in the terminal UI. Must be used with `persistent`.

This option is most useful for scripts that can be manipulated while they are running, like Jest or Vitest.

```jsonc title="./turbo.json"
{
  "tasks": {
    "test:watch": {
      "interactive": true,
      "persistent": true,
    },
  },
}
```

interruptible [#interruptible]

Default: `false`

Label a `persistent` task as `interruptible` to allow it to be restarted by `turbo watch`.

`turbo watch` watches for changes to your packages and automatically restarts tasks
that are affected. However, if a task is persistent, it will not be restarted by default.
To enable restarting persistent tasks, set `interruptible` to `true`.

with [#with]

A list of tasks that will be ran alongside this task. This is most useful for long-running tasks that you want to ensure always run at the same time.

```json title="./apps/web/turbo.json"
{
  "tasks": {
    "dev": {
      "with": ["api#dev"],
      "persistent": true,
      "cache": false
    }
  }
}
```

Boundaries [#boundaries]

The `boundaries` tag allows you to define rules for the [`boundaries` command](/docs/reference/boundaries).

```json title="./turbo.json"
{
  "boundaries": {}
}
```

tags [#tags]

Each key in the `tags` object is the name of a tag that can be checked with [`turbo boundaries`](/docs/reference/boundaries).

In the configuration object for a tag, you can define rules for dependencies and dependents.

dependencies and dependents [#dependencies-and-dependents]

Rules for a tag's dependencies and dependents.

You can add an allowlist and a denylist:

```jsonc title="./turbo.json"
{
  "boundaries": {
    "utils": {
      "dependencies": {
        // permit only packages with the `ui` tag
        "allow": ["ui"],
        // and ban packages with the `unsafe` tag
        "deny": ["unsafe"],
      },
    },
  },
}
```

Both the allowlist and the denylist can be omitted.

```jsonc title="./turbo.json"
{
  "boundaries": {
    "utils": {
      "dependencies": {
        // only packages with the `unsafe` tag are banned, all other packages permitted
        "deny": ["unsafe"],
      },
    },
  },
}
```

Rules can also be added for a tag's dependents, i.e. packages that import this tag.

```jsonc title="./turbo.json"
{
  "boundaries": {
    "utils": {
      "dependents": {
        // only packages with the `web` tag can import packages with the `utils` tag
        "allow": ["web"],
      },
    },
  },
}
```

Remote caching [#remote-caching]

The global `remoteCache` option has a variety of fields for configuring remote cache usage

```jsonc title="./turbo.json"
{
  "remoteCache": {},
}
```

enabled [#enabled]

Default: `true`

Enables remote caching.

When `false`, Turborepo will disable all remote cache operations, even if the repo has a valid token.
If true, remote caching is enabled, but still requires the user to login and link their repo to a remote cache.

signature [#signature]

Default: `false`

Enables signature verification for requests to the remote cache.
When `true`, Turborepo will sign every uploaded artifact using the value of the environment variable `TURBO_REMOTE_CACHE_SIGNATURE_KEY`.
Turborepo will reject any downloaded artifacts that have an invalid signature or are missing a signature.

preflight [#preflight]

Default: `false`

When enabled, any HTTP request will be preceded by an OPTIONS request to determine if the request is supported by the endpoint.

timeout [#timeout]

Default: `30`

Sets a timeout for remote cache operations.
Value is given in seconds and only whole values are accepted.
If `0` is passed, then there is no timeout for any cache operations.

uploadTimeout [#uploadtimeout]

Default: `60`

Sets a timeout for remote cache uploads.
Value is given in seconds and only whole values are accepted.
If `0` is passed, then there is no timeout for any remote cache uploads.

apiUrl [#apiurl]

Default: `"https://vercel.com"`

Set endpoint for API calls to the remote cache.

loginUrl [#loginurl]

Default: `"https://vercel.com"`

Set endpoint for requesting tokens during `turbo login`.

teamId [#teamid]

The ID of the Remote Cache team.
Value will be passed as `teamId` in the querystring for all Remote Cache HTTP calls.
Must start with `team_` or it will not be used.

teamSlug [#teamslug]

The slug of the Remote Cache team.
Value will be passed as `slug` in the querystring for all Remote Cache HTTP calls.

Future flags [#future-flags]

Future flags allow you to opt-in to experimental features before they become the default behavior.

```jsonc title="./turbo.json"
{
  "futureFlags": {
    "experimentalObservability": true
  }
}
```

experimentalObservability [#experimentalobservability]

Default: `false`

When enabled, Turborepo will honor the `experimentalObservability.otel` configuration block (if present) to send run summaries to an OpenTelemetry Protocol (OTLP) collector.

<Callout type="info">
  Invalid or incomplete OTEL configuration (e.g., missing endpoint, invalid
  protocol) results in metrics being disabled rather than causing runs to fail.
  Turborepo will continue executing tasks normally even if the exporter cannot
  be initialized.
</Callout>

Experimental observability [#experimental-observability]

<ExperimentalBadge>
  Experimental
</ExperimentalBadge>

Configure Turborepo to export metrics to observability backends like Datadog, Prometheus, or other OTLP-compatible collectors.

experimentalObservability [#experimentalobservability-1]

The `experimentalObservability` configuration requires `futureFlags.experimentalObservability` to be set to `true` in your root `turbo.json`. This applies to all configuration sources, including the `turbo.json` block, environment variables, and CLI flags. Turborepo will error if observability is configured without the future flag enabled.

```jsonc title="./turbo.json"
{
  "futureFlags": {
    "experimentalObservability": true
  },
  "experimentalObservability": {
    "otel": {
      "enabled": true,
      "protocol": "grpc",
      "endpoint": "https://api.datadoghq.com/api/v2/otlp",
      "headers": {
        "X-Custom-Header": "value"
      },
      "timeoutMs": 10000,
      "intervalMs": 15000,
      "resource": {
        "service.name": "turborepo"
      },
      "metrics": {
        "runSummary": true,
        "taskDetails": false,
        "taskAttributes": {
          "id": true,
          "hashes": false
        }
      },
      "useRemoteCacheToken": true
    }
  }
}
```

experimentalObservability.otel.enabled [#experimentalobservabilityotelenabled]

Default: `true` (when endpoint is provided)

Enable or disable the OpenTelemetry metrics exporter.

experimentalObservability.otel.protocol [#experimentalobservabilityotelprotocol]

Default: `"grpc"`

The OTLP protocol to use. Supported values:

* `"grpc"` - OTLP over gRPC
* `"http/protobuf"` - OTLP over HTTP with protobuf encoding

experimentalObservability.otel.endpoint [#experimentalobservabilityotelendpoint]

**Required** when using file-based configuration.

The OTLP collector endpoint URL. For example:

* Datadog: `"https://api.datadoghq.com/api/v2/otlp"`
* Custom collector: `"https://otel-collector.example.com:4317"` (gRPC) or `"https://otel-collector.example.com:4318"` (HTTP)

If the endpoint is missing or empty when OTEL is enabled, the exporter will not be initialized and metrics will be disabled. The run will continue normally.

<Callout type="warn">
  OTEL endpoints must use `https://`. `http://` endpoints are rejected to ensure
  metrics and authentication headers are sent over TLS.
</Callout>

experimentalObservability.otel.headers [#experimentalobservabilityotelheaders]

Optional HTTP headers to include with export requests. Useful for authentication (e.g., API keys) or custom metadata.

```jsonc title="./turbo.json"
{
  "experimentalObservability": {
    "otel": {
      "headers": {
        "X-Custom-Header": "value"
      }
    }
  }
}
```

<Callout type="warn">
  Avoid storing tokens or API keys in `turbo.json`. `turbo.json` is often
  committed to source control, so prefer environment variables or your CI
  secret manager for sensitive header values.
</Callout>

experimentalObservability.otel.timeoutMs [#experimentalobservabilityoteltimeoutms]

Default: `10000` (10 seconds)

Timeout in milliseconds for export requests to the collector.

experimentalObservability.otel.intervalMs [#experimentalobservabilityotelintervalms]

Default: `15000` (15 seconds)

Interval in milliseconds between periodic exports to the collector. This controls how frequently metrics are batched and sent to the OTLP collector during a run. A final flush is always performed when the run completes, regardless of this setting.

experimentalObservability.otel.resource [#experimentalobservabilityotelresource]

Optional resource attributes to attach to all exported metrics. These help identify the source of metrics in your observability platform.

```jsonc title="./turbo.json"
{
  "experimentalObservability": {
    "otel": {
      "resource": {
        "service.name": "turborepo",
        "service.namespace": "ci",
        "deployment.environment": "production"
      }
    }
  }
}
```

experimentalObservability.otel.metrics [#experimentalobservabilityotelmetrics]

Control which metric groups are exported.

metrics.runSummary [#metricsrunsummary]

Default: `true`

Export run-level metrics:

* Run duration
* Tasks attempted, failed, and cached
* Exit code
* SCM branch and revision

metrics.taskDetails [#metricstaskdetails]

Default: `false`

Export per-task metrics:

* Task execution duration
* Cache hit/miss status and source
* Cache time saved
* Task identifiers (task ID, package, hash)

metrics.taskAttributes [#metricstaskattributes]

Control which task attributes are included in per-task metrics. These options only take effect when `taskDetails` is enabled.

taskAttributes.id [#taskattributesid]

Default: `false`

Include the task ID attribute on per-task metrics.

taskAttributes.hashes [#taskattributeshashes]

Default: `false`

Include task hash attributes on per-task metrics.

experimentalObservability.otel.useRemoteCacheToken [#experimentalobservabilityoteluseremotecachetoken]

Default: `false`

When enabled, automatically adds an `Authorization: Bearer <token>` header to OTLP export requests using your existing remote cache credentials (from `turbo login` or the `TURBO_TOKEN` environment variable). Existing `Authorization` headers in the `headers` configuration are preserved and take precedence.

This is useful when your OTLP collector uses the same authentication as your remote cache.

<Callout type="info">
  Exporter failures are logged but do not cause the run to fail. If the
  collector is unavailable or misconfigured, Turborepo will continue executing
  tasks normally. Invalid or incomplete configuration (e.g., missing endpoint
  when enabled) results in the exporter not being initialized, and metrics will
  be disabled for that run.
</Callout>

Environment variables [#environment-variables]

You can also configure observability via environment variables, which take precedence over `turbo.json` settings:

* `TURBO_EXPERIMENTAL_OTEL_ENABLED` - Enable/disable exporter (`1` or `0`)
* `TURBO_EXPERIMENTAL_OTEL_PROTOCOL` - Protocol (`grpc` or `http/protobuf`)
* `TURBO_EXPERIMENTAL_OTEL_ENDPOINT` - Collector HTTPS endpoint URL
* `TURBO_EXPERIMENTAL_OTEL_TIMEOUT_MS` - Timeout in milliseconds
* `TURBO_EXPERIMENTAL_OTEL_INTERVAL_MS` - Export interval in milliseconds
* `TURBO_EXPERIMENTAL_OTEL_HEADERS` - Comma-separated key=value pairs (e.g., `"Header=value,Other=value"`)
* `TURBO_EXPERIMENTAL_OTEL_RESOURCE` - Comma-separated key=value pairs for resource attributes
* `TURBO_EXPERIMENTAL_OTEL_METRICS_RUN_SUMMARY` - Enable run summary metrics (`1` or `0`)
* `TURBO_EXPERIMENTAL_OTEL_METRICS_TASK_DETAILS` - Enable task details metrics (`1` or `0`)
* `TURBO_EXPERIMENTAL_OTEL_METRICS_TASK_ATTRIBUTES_ID` - Include task ID attribute (`1` or `0`)
* `TURBO_EXPERIMENTAL_OTEL_METRICS_TASK_ATTRIBUTES_HASHES` - Include task hash attributes (`1` or `0`)
* `TURBO_EXPERIMENTAL_OTEL_USE_REMOTE_CACHE_TOKEN` - Use remote cache token for OTLP authentication (`1` or `0`)

CLI flags [#cli-flags]

You can override observability settings via CLI flags:

* `--experimental-otel-enabled` - Enable/disable exporter
* `--experimental-otel-protocol` - Protocol (`grpc` or `http/protobuf`)
* `--experimental-otel-endpoint` - Collector HTTPS endpoint URL
* `--experimental-otel-timeout-ms` - Timeout in milliseconds
* `--experimental-otel-interval-ms` - Export interval in milliseconds
* `--experimental-otel-header KEY=VALUE` - Add HTTP header (can be repeated)
* `--experimental-otel-resource KEY=VALUE` - Add resource attribute (can be repeated)
* `--experimental-otel-metrics-run-summary` - Enable run summary metrics
* `--experimental-otel-metrics-task-details` - Enable task details metrics
* `--experimental-otel-use-remote-cache-token` - Use remote cache token for OTLP authentication

---

[View full sitemap](/sitemap.md)