apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx
Turborepo speeds up builds, lints, tests, and any other tasks that you need to do in your Continuous Integration pipelines. Through parallelization and Remote Caching, Turborepo makes your CI dramatically faster.
For examples of how to connect your CI vendor to Remote Cache and run tasks, visit our CI guides.
To enable Remote Caching for your CI, setup the environment variables for Turborepo to access your Remote Cache.
| Environment Variable | Description |
|---|---|
TURBO_TOKEN | The Bearer token to access the Remote Cache |
TURBO_TEAM | The account name associated with your repository. When using Vercel Remote Cache, this is your team's slug. |
When you run tasks through turbo, your CI will be able to hit cache, speeding up your pipelines.
For self-hosted Remote Cache options, visit Turborepo's Remote Cache documentation.
</Callout>By installing turbo globally onto your development and CI machines, you can use one mental model to run your entire repository, from development to ship. The tasks that you've registered in your turbo.json will work exactly the same in CI.
You can filter your tasks using the --filter flag exactly the same as when you're working with turbo locally. Filtering by packages, directories, and Git history are all supported in CI.
You can also use the --affected flag to only run tasks in packages that have changes.
Docker is an important part of many deployment pipelines. Turborepo's prune subcommand helps you ship lightweight images by removing unnecessary dependencies and code from your images.
For more on how to deploy from a Turborepo with Docker, visit the dedicated Docker guide.
You can use the --affected flag to only run tasks that have changes.
turbo run build --affected
You'll want to use this flag in situations like:
--affected will can handle shallow clones more gracefully than bespoke filtering because it falls back to running all tasks.--affected in GitHub ActionsCI/CD pipelines are a perfect place to use --affected. With --affected, Turborepo can automatically detect that you're running in GitHub Actions by inspecting environment variables set by GitHub, like GITHUB_BASE_REF.
In the context of a PR, this means that Turborepo can determine which packages have changed between the PR's base branch and the PR's head branch. This allows you to run tasks only for the packages that are affected by the changes in the PR.
While GITHUB_BASE_REF works well in pull_request and pull_request_target events, it is not available during regular push events. In those cases, we use GITHUB_EVENT_PATH to determine the base branch to compare your commit to. In force pushes and pushing branch with no additional commits, we compare to the parent of the first commit on the branch.
turbo query affectedAs your codebase and CI grow, you may start to look for more ways to get even faster. While hitting cache is useful, you also may be able to skip work entirely. Using turbo query affected, you can skip lengthy container preparation steps like dependency installation that will end up resulting in a cache hit, anyway.
Start by cloning your repository. A clone with sufficient history is necessary for comparisons — if the checkout is too shallow, all packages will be considered changed. For example, cloning with --filter=blob:none --depth=0 will ensure the right history is available.
By default, turbo query affected compares against the merge-base with your default branch.
Use turbo query affected to check if a package has changes. The command outputs JSON that you can parse with a tool like jq:
<Tabs items={["Check a package", "Check a specific task", "Quick check with --exit-code"]}> <Tab value="Check a package">
affected=$(turbo query affected --packages web)
count=$(echo "$affected" | jq '.data.affectedPackages.length')
if [ "$count" -gt 0 ]; then
echo "web is affected, proceeding with build"
# Run your build steps
else
echo "web is not affected, skipping"
exit 0
fi
Check if the test task for the docs package is affected. This respects inputs configuration, so only relevant file changes are considered:
affected=$(turbo query affected --tasks test --packages docs)
count=$(echo "$affected" | jq '.data.affectedTasks.length')
if [ "$count" -gt 0 ]; then
echo "docs#test is affected, proceeding"
else
echo "docs#test is not affected, skipping"
exit 0
fi
If you only need a binary "affected or not" signal, use --exit-code as a shorthand. It exits with code 1 when results are found, 0 when nothing is affected, or 2 on errors:
turbo query affected --packages web --exit-code
The JSON output includes a length field that tells you how many packages or tasks are affected. When length is 0, you can safely skip the rest of your CI pipeline.
The output also includes the reason each package is affected (e.g., FileChanged, DependencyChanged), which is useful for debugging unexpected builds.
Turborepo's caching abilities allow you to create fast CI pipelines with minimal complexity. Through Remote Caching and using the --filter flag to target packages for builds, Turborepo will handle change detection for large monorepos with little overhead.
For example, your CI could run these two commands to quickly handle quality checks and build your target application:
turbo run lint check-types test: Run quality checks for your entire repository. Any packages that haven't changed will hit cache.turbo build --filter=web: Build the web package using the build task you've registered in turbo.json. If the web package or its dependencies haven't changed, the build will also hit cache.As your codebase scales, you may find more specific opportunities to optimize your CI - but relying on caching is a great place to start.
turbo in CIUsing global turbo is convenient in CI workflows, allowing you to easily run commands specific to your CI and take advantage of Automatic Workspace Scoping.
However, in some cases, you may be running turbo commands or scripts that use turbo before installing packages with your package manager. One example of this is using turbo prune to create a Docker image. In this situation, global turbo will not be able to use the version from package.json because the binary for that version hasn't been installed yet.
For this reason, we encourage you to pin your global installation of turbo in CI to the major version in package.json since breaking changes will not be introduced within a major version. You could additionally opt for added stability by pinning an exact version, trading off for maintenance burden to receive bug fixes in patch releases.
turbo run in CIturbo run is the most common command you will use when working in your Turborepo so it is aliased to turbo for convenience. While this is great for working locally, there are other subcommands for turbo like turbo prune and turbo generate.
We're always working to make turbo better so we may add more subcommands in the future. For this reason, you can prevent naming collisions by using turbo run in your CI.
As an example, if you have a turbo deploy command in your CI pipelines, it may conflict with a potential deploy subcommand built directly into the turbo CLI. To avoid this, use turbo run deploy in your CI pipeline instead.
If your task is passing when you miss cache but failing when you hit cache, you likely haven't configured the outputs key for your task correctly.
If you haven't defined the env or globalEnv keys for your task, Turborepo will not be able to use them when creating hashes. This means your task can hit cache despite being in a different environment.
Check your configuration for the env and globalEnv keys.
You now have everything you need to ship applications with Turborepo. To learn more about specific use cases, check the Guides or dive deeper into core concepts.