Configuring Tasks
Purpose and Scope
Task configuration is where a Turborepo workspace turns ordinary package scripts into a coordinated build, test, lint, and development workflow. A task is a script that Turborepo runs, and the root turbo.json file is where those tasks are registered. Once registered, each key in the tasks object can be executed with turbo run, and Turborepo searches workspace packages for matching package.json scripts. The design goal is not to serialize all work; it is to describe the few relationships that must be respected so Turborepo can safely parallelize everything else.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/running-tasks.mdx
This guide focuses on designing task definitions rather than merely listing configuration fields. A useful task definition answers four questions: what package scripts should be eligible to run, what other tasks must finish first, what files represent successful output, and whether the default inputs are sufficient for a correct cache key. Those answers determine execution order, cache behavior, and developer experience in local terminals and CI pipelines. If you are adopting Turborepo in an existing repository, start with a root turbo.json; if you are starting fresh, the docs recommend experimenting from a create-turbo repository.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx
Relevant Source Files
apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx- Primary guide for registering tasks inturbo.json, usingdependsOn, declaring outputs and inputs, and explaining why an empty task definition is usually incomplete.apps/docs/content/docs/crafting-your-repository/running-tasks.mdx- Shows how configured tasks are invoked through rootpackage.jsonscripts, globalturbo, automatic package scoping, and filters.apps/docs/content/docs/crafting-your-repository/caching.mdx- Explains why task outputs and inputs matter, how Turborepo fingerprints work, and why deterministic tasks are required for reliable cache hits.apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx- Connects task configuration to CI execution, Remote Caching, filters, and affected-task workflows.apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx- Provides the package graph context for internal packages whosebuildscripts often need dependency-aware ordering.apps/docs/content/docs/crafting-your-repository/developing-applications.mdx- Defines long-running development tasks withcache: false,persistent: true, setup dependencies, and filtering for specific applications.
Core Task Model
The most important mental model is that turbo.json does not replace package.json scripts. Instead, it registers task names and gives Turborepo enough metadata to run matching scripts across the package graph. For example, a build task in turbo.json targets packages that define a build script. If several packages have that script, Turborepo can run them concurrently unless dependency relationships say otherwise. This is why a bare build task with an empty object is technically runnable but usually unsafe: it provides no ordering rule and no cacheable file outputs.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
The package graph is what makes dependency-aware task ordering possible. Internal packages are discovered through workspace package metadata and their package.json dependency relationships. When an application imports a library package, Turborepo can understand that the library is a dependency of the application. A build pipeline should normally compile the dependency before the dependent application, while unrelated packages can still run in parallel. This lets teams keep the ergonomics of normal workspace packages while getting a faster execution plan than sequential workspace commands.
Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
Designing Dependencies, Outputs, and Inputs
Use dependsOn to express ordering constraints, not to manually schedule every task. The common dependency-aware build rule is "dependsOn": ["^build"]. The caret microsyntax means the target package's direct dependencies should run their build task before the target package runs its own build task. This models the usual relationship between compiled internal libraries and applications. Avoid adding dependencies that are merely habitual or cosmetic; every dependency edge reduces available parallelism, so the best configuration captures correctness requirements while leaving independent work free to run at the same time.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
Declare outputs for tasks that produce files you want Turborepo to cache and restore. Caching works by fingerprinting a known set of inputs, storing task results, and later restoring those results when the same inputs appear again. If a build creates compiled files but the task has no outputs configured, Turborepo may run the script but will not have the expected file artifacts to restore. Outputs should describe the files that prove the task completed successfully, while excluding tool-specific transient caches when those caches are not useful or safe to restore.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx
Inputs decide what changes should invalidate a cached result. Turborepo assumes cacheable tasks are deterministic: given the same relevant files and environment, they should produce the same outputs. If a task reads generated files, configuration files, or other non-obvious inputs, include them so the fingerprint changes when those files change. Conversely, broad input patterns may invalidate too often and reduce cache value. Good task design therefore balances correctness and reuse: include every file that can change the result, but avoid pulling unrelated repository noise into the task hash.
Sources: apps/docs/content/docs/crafting-your-repository/caching.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
Common Configuration Patterns
A typical production-oriented configuration starts with a dependency-aware build task, then adds independent tasks such as lint and test as needed. The exact outputs depend on your framework and compiler, but the important pattern is that build tasks produce cacheable artifacts while lint tasks often produce no durable output. You can then expose common workflows from the root package.json with commands like turbo run build, turbo run test, and turbo run lint. The running-tasks guide recommends putting these commands only in the root package to avoid recursive turbo calls inside packages.
Sources: apps/docs/content/docs/crafting-your-repository/running-tasks.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"lint": {},
"test": {
"dependsOn": ["build"]
}
}
}{
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint"
}
}Development tasks have a different shape because they are usually long-lived processes. A dev task should commonly disable caching and mark itself as persistent. Disabling caching communicates that the output of a changing development server is not a reusable artifact. Marking the task as persistent tells Turborepo and its terminal UI that the process is expected to keep running, and it prevents accidental dependency chains that wait on a task that will not exit. This distinction keeps production tasks cacheable while preserving an interactive local development loop.
Sources: apps/docs/content/docs/crafting-your-repository/developing-applications.mdx
{
"tasks": {
"dev": {
"cache": false,
"persistent": true
}
}
}When a development server needs setup work first, model the setup as a separate task and depend on it. The development guide shows this with a root task named //#dev:setup, which can produce files such as code generation output before dev begins. Root tasks are useful for repository-level work that is not owned by a single package. The same idea can be applied to package-specific setup when the work belongs to a package script. The key is to make setup explicit rather than hiding it inside a long-running command.
Sources: apps/docs/content/docs/crafting-your-repository/developing-applications.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx
{
"tasks": {
"dev": {
"cache": false,
"persistent": true,
"dependsOn": ["//#dev:setup"]
},
"//#dev:setup": {
"outputs": [".codegen/**"]
}
}
}Package-Specific Overrides and Entry Points
Package-specific configuration is useful when one workspace member behaves differently from the default task contract. Most packages may emit dist/**, while a web application may emit a framework-specific directory, or a documentation app may need a setup step before development. In those cases, keep the shared task definition simple and override only the task that differs. The docs also point to arbitrary package tasks and root tasks as ways to express targeted relationships without forcing every package to adopt the same script shape.
Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx
Task configuration also works with entry-point filtering. The running and development guides show that --filter can select a subset of the package graph, such as a single application and the packages it depends on. This is important because well-designed tasks should be reusable in many contexts: a developer can run turbo dev --filter=web, while CI can run broader build and test commands. The same turbo.json rules apply in both cases, so filtering should narrow the graph without changing the correctness of dependencies or cache metadata.
Sources: apps/docs/content/docs/crafting-your-repository/running-tasks.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx, apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx
turbo dev --filter=web
turbo run build --filter=@repo/uiCI and Caching Signals
The same task definitions should run in CI as they do locally. CI pipelines benefit from parallelization, task filtering, and Remote Caching, but those benefits depend on accurate task metadata. The constructing-CI guide recommends configuring Remote Cache access with environment variables such as TURBO_TOKEN and TURBO_TEAM, then running the same registered tasks through turbo. CI can also filter by packages, directories, Git history, or affected work when history is available. This makes task configuration the shared contract between local development and release automation.
Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx
A good cache signal is boring: the first run misses because the fingerprint is new, and later equivalent runs restore outputs instead of rebuilding. If cache hits are surprising or wrong, revisit outputs and inputs before changing CI structure. A task that reads undeclared files can reuse stale results, while a task that writes undeclared artifacts may appear to succeed but leave missing files after restoration. Treat cacheability as part of the task definition, not an afterthought. This is especially important for shared internal packages, where stale compiled output can affect multiple downstream applications.
Sources: apps/docs/content/docs/crafting-your-repository/caching.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx
Compact Reference
| Concept | Configuration surface | Use it when |
|---|---|---|
| Registered task | tasks.<name> in turbo.json | A package script with the same name should be runnable through turbo run. |
| Dependency task | dependsOn | One task must complete before another task starts. |
| Dependency package task | ^task microsyntax | A package's dependencies must run the same task before the package does. |
| Cache outputs | outputs | A task creates files that should be stored and restored from cache. |
| Cache inputs | inputs | A task reads files beyond the default relevant set or needs a narrower fingerprint. |
| Development task | cache: false and persistent: true | A task is long-running, interactive, or not useful to cache. |
| Root task | //#task-name | Repository-level work should be scheduled outside any individual package. |
| Entry-point narrowing | --filter | A workflow should run for a package, directory, or affected subset of the graph. |
Next Steps
After defining your first tasks, run them from the root with explicit turbo run commands and confirm the order, output, and cache behavior match your intent. Add dependency edges only when required, declare outputs for file-producing tasks, and keep development tasks separate from cacheable build tasks. Then read the running-tasks and caching guides to refine day-to-day commands and troubleshoot cache behavior. For deeper reference work, continue to the turbo.json configuration and turbo run reference pages so each option is understood before it becomes part of CI.