---
title: Running Tasks
description: Turborepo can run all your tasks.
---

import { Callout } from "../../../../../components/Callout";
import HeartIcon from "@heroicons/react/solid/HeartIcon";
import { Tabs, Tab } from '../../../../../components/Tabs'

# Running Tasks in a Monorepo

Every monorepo has two main building blocks: **workspaces** and **tasks**. Let's imagine you have a monorepo containing **three workspaces**, each with **three tasks**:

![](/images/docs/your-monorepo-excalidraw.png)

Here, both `apps/web` and `apps/doc` use code from `packages/shared`. In fact, when they're built (via `build`) **they need `packages/shared` to be built _first_**.

## Most tools don't optimize for speed

Let's imagine we want to run all our tasks across all our workspaces. With a tool like `yarn`, you might run a script like this:

```
yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build
```

This would mean the tasks run like this:

![](/images/docs/yarn-workspaces-excalidraw.png)

As you can see, `lint` gets run in all the workspaces. Then, `build` gets run - with `shared` going first. Finally, `test` gets run.

**This is the slowest possible way to run these tasks**. Each task needs to wait for the previous one to finish before it can start. To improve on this, we'll need a tool that can multitask.

## Turborepo can multitask

Turborepo can schedule our tasks for maximum speed by understanding the dependencies between our tasks.

First, we declare our tasks inside `turbo.json`:

```json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"],
      // ^build means `build` must be run in dependencies
      // before it can be run in this workspace
      "dependsOn": ["^build"]
    },
    "test": {},
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
```

Next, we can replace our `yarn workspaces` script with this:

```diff
- yarn workspaces run lint
- yarn workspaces run test
- yarn workspaces run build
+ turbo run lint test build
```

When we run it, Turborepo will **multitask as many tasks as possible over all available CPU's**, meaning our tasks run like this:

![](/images/docs/turborepo-excalidraw.png)

Both `lint` and `test` run immediately, because they have no `dependsOn` specified in `turbo.json`.

The `build` task inside `shared` completes first, then `web` and `docs` build afterwards.

## Defining a pipeline

The `pipeline` configuration declares which tasks depend on each other in your
monorepo. Here's a kitchen sink example:

```jsonc filename="turbo.json"
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      // A workspace's `build` task depends on that workspace's
      // topological dependencies' and devDependencies'
      // `build` tasks  being completed first. The `^` symbol
      // indicates an upstream dependency.
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "deploy": {
        // A workspace's `deploy` task depends on the `build`,
        // `test`, and `lint` tasks of the same workspace
        // being completed.
        "dependsOn": ["build", "test", "lint"]
    },
    "test": {
      // A workspace's `test` task depends on that workspace's
      // own `build` task being completed first.
      "dependsOn": ["build"],
      // A workspace's `test` task should only be rerun when
      // either a `.tsx` or `.ts` file has changed.
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    },
    // A workspace's `lint` task has no dependencies and
    // can be run whenever.
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
```

You can read more about how to setup your tasks in the next section on [Task Dependencies](/repo/docs/core-concepts/monorepos/task-dependencies).

## Running tasks from the root

`turbo` can run tasks that exist in the `package.json` file at the root of the monorepo.
These must be explicitly added to the pipeline configuration using the key syntax `"//#<task>"`. This is
true even for tasks that already have their own entry. For example, if your pipeline declares a `"build"` task,
and you want to include the `build` script defined in the monorepo's root `package.json` file with
`turbo run build`, you must opt the root into it by declaring `"//#build": {...}` in your configuration.
Conversely, you _do not_ need to define a generic `"my-task": {...}` entry if all you need is `"//#my-task": {...}`.

A sample pipeline that defines the root task `format` and opts the root into `test` might look like:

```jsonc filename="turbo.json"
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "test": {
      "dependsOn": ["^build"],
    },
    // This will cause the "test" script to be included when
    // "turbo run test" is run
    "//#test": {
      "dependsOn": [],
    },
    // This will cause the "format" script in the root package.json
    // to be run when "turbo run format" is run. Since the general
    // "format" task is not defined, only the root's "format" script
    // will be run.
    "//#format": {
      "dependsOn": [],
      "outputs": ["dist/**/*"],
      "inputs": ["version.txt"]
    }
  }
}
```

**A note on recursion**: Scripts defined in the monorepo's root `package.json` often call `turbo` themselves.
For example, the `build` script might be `turbo run build`. In this situation, including `//#build` in
`turbo run build` will cause infinite recursion. It is for this reason that tasks run from the monorepo's root must
be explicitly opted into via including `//#<task>` in the pipeline configuration. `turbo` includes
some best-effort checking to produce an error in the recursion situations, but it is up to you to only
opt in those tasks which don't themselves trigger a `turbo` run that would recurse.

### Incremental Adoption

After you've declared a task in `turbo.json`, it's up to you to implement it in
your `package.json` manifests. You can add scripts all at once, or one workspace
at at a time. Turborepo will gracefully skip workspaces that don't include the
task in their respective package.json manifest.

For example, if your repository has the three workspaces (similar to the ones mentioned above):

```bash
apps/
  web/package.json
  docs/package.json
packages/
  shared/package.json
turbo.json
package.json
```

where `turbo.json` declares a `build` task, but only two `package.json`'s implement that `build`
task:

<Tabs items={['turbo.json', 'web', 'docs', 'shared']} storageKey="skipped-tasks-example">
  <Tab>
  ```jsonc filename="turbo.json"
  {
    "$schema": "https://turbo.build/schema.json",
    "pipeline": {
      "build": {
        "outputs": [".next/**", "!.next/cache/**", "dist/**"]
      }
    }
  }
  ```
  </Tab>
  <Tab>
   ```jsonc filename="apps/web/package.json"
  {
    "name": "web",
    "scripts": {
      "build": "next build"
    }
  }
  ```
  </Tab>
  <Tab>
   ```jsonc filename="apps/docs/package.json"
  {
    "name": "docs",
    "scripts": {
      "build": "vite build"
    }
  }
  ```
  </Tab>
  <Tab>
  ⚠️ Note the missing `build` script!
   ```jsonc filename="packages/shared/package.json"
  {
    "name": "shared",
    "scripts": {}
  }
  ```
  </Tab>
</Tabs>

```bash
turbo run build
```

A turbo build will only execute the `build` script for the `web` and `docs` workspaces. The
`shared` package will still be part of the task graph, but will gracefully be skipped.

<Callout
  type="idea"
  icon={<HeartIcon className="h-5 w-5 mt-1 text-gray-400" aria-hidden="true" />}
>
  Turborepo's Pipeline API design and this page of documentation was inspired by
  [Microsoft's Lage
  project](https://microsoft.github.io/lage/docs/Tutorial/pipeline#defining-a-pipeline).
  Shoutout to [Kenneth Chau](https://x.com/kenneth_chau) for the idea of
  fanning out tasks in such a concise and elegant way.
</Callout>
