Repo
Docs
Core Concepts
Environment Variable Inputs

Environment Variable Inputs

Because environment variables are not captured in source code, they're not as easily shared across machines. Ensuring a consistent environment setup across different machines for developers and CI is a difficult task. Turborepo provides you with the tools to express which environment variables your application depends on.

Configuration

Turborepo enables you to directly enumerate which environment variables should be considered for the hash key, both at a global level and a pipeline level.

{
  "$schema": "https://turbo.build/schema.json",
  "globalEnv": ["API_BASE_URL"],
  "pipeline": {
    "test": {
      "env": ["MOCHA_REPORTER"]
    },
    //...
  }
}

In this example we can imagine an application which has a different API_BASE_URL in the test environment, staging environment, and production environment. This configuration would make sure that the value of API_BASE_URL is considered for the hash, and if it is different, the task will not be restored from cache.

Further, we can see that test tasks want different caching behavior based upon the value of MOCHA_REPORTER, this can be used to enable CI to integrate with different services than local development.

globalEnv

Environment variables included in globalEnv key will impact the hashes of all tasks.

pipeline.<task>.env

Environment variables included in pipeline.<task>.env will impact the hashes of only that task.

Wildcards

Every place in turbo.json that accepts environment variables accepts wildcards, including the task-level env and global-level globalEnv. This enables you to easily specify patterned names of environment variables with both inclusions and exclusions:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "env": ["NEXT_PUBLIC_*", "!NEXT_PUBLIC_GIT_*"]
    }
  }
}

Exclusion patterns apply only to the set of variables matched via inclusions.

Be careful about the combination of wildcards and CI environments. Some CI environments set additional environment variables such as NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA which will prevent ever getting a cache hit. After configuring wildcards be sure to carefully review a run summary from all of your environments in order to make sure that it is only including variables you care about.

Syntax

Since these values are specified in JSON, which also uses the \ as an escape you'll need to make certain that the value that you pass in resolves to the escaped string.

  • A * anywhere in the pattern will match zero or more characters.
  • A leading ! means that the entire pattern will be negated.
  • A ! in any position other than the first position is a literal !.
  • Characters with special meaning (leading ! and *) may be escaped by placing a \ in front of them to get a literal value.

Examples

  • "*": matches every environment variable.
  • "!*": excludes every environment variables.
  • "FOO*": matches FOO, FOOD, FOO_FIGHTERS, etc.
  • "FOO\*": because this is specified in JSON, this 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": because this is specified in JSON, this resolves to "!FOO", and excludes from the match set a single environment variable named !FOO.
  • "\\!FOO": matches a single environment variable named !FOO.
  • "FOO!": matches a single environment variable named FOO!.

Loose & Strict Environment Modes

Turborepo offers two different modes for handling environment variable in tasks: loose mode and strict mode. The mode controls which environment variables are made available to each task at execution time.

Loose Mode

Loose mode, the default behavior, does not filter any environment variables. Therefore, all environment variables from the machine are made available to every single task. This ensures the greatest compatibility while accepting some risk that a task will implicitly have access to an unspecified environment variable. A user may run a task with a different value of an environment variable, but incorrectly see FULL TURBO because the environment variable was not configured in turbo.json.

Strict Mode

Strict mode, which can be enabled with turbo --env-mode=strict, filters out environment variables.

Only important system environment variables and environment variables configured inside of turbo.json will be made available to a task.

If you want an environment variable to be part of the cache key, use Hashed Environment Variables.

If you do not want an environment variable to be part of the cache key, use Unhashed Environment Variables.

System Environment Variables

Turborepo passes in the following environment variables to all tasks, even in strict mode:

  • PATH
  • SHELL
  • SYSTEMROOT

Hashed Environment Variables

Hashed environment variables, defined in globalEnv and env, will be made available to tasks and included in the hashed cache key.

Environment variables such as NODE_ENV whose value can change the task outputs, should be included in globalEnv or env.

Unhashed Environment Variables

Unhashed environment variables, defined in globalPassThroughEnv and passThroughEnv, will be made available to tasks but not included in the hashed cache key.

Access tokens such as NPM_TOKEN that change per-user, but result in the same task outputs, should be defined in globalPassThroughEnv and passThroughEnv.

Infer Mode

Turborepo enables incremental adoption of strict mode by inferring whether or not you want strict behavior from your configuration.

  • "globalPassThroughEnv": []. Strict mode is enabled for every task. No environment variables are allowlisted.
  • "passThroughEnv": []. Strict mode is enabled for the specific task. No environment variables are allowlisted.

.env Files

Turborepo does not load .env files into the environment! Your task must handle loading of the .env files itself.

Frameworks commonly use dotenv (opens in a new tab) to automatically load environment variables for a task. This can make it hard for Turborepo to understand the environment of your task by default:

  • .env files store environment variables in a file rather than in the environment.
  • environment variables from this file are loaded after Turborepo has already started execution of the task.
  • the file is often specified in .gitignore, so Turborepo does not include it in the task hash by default.

Given the complexity of configuring this correctly using just file inputs, Turborepo explicitly supports the .env file pattern using the fields globalDotEnv and dotEnv inside of turbo.json. To have Turborepo consider the appropriate set of .env files, specify them inside of turbo.json. The below is the correct dotEnv configuration for a Next.js application:

In most cases, this configuration should be matched to your framework's behavior, not the particular configuration you're presently using for your application.

{
  "$schema": "https://turbo.build/schema.json",
  "globalDotEnv": [".env"],
  "pipeline": {
    "build": {
      "dotEnv": [".env.production.local", ".env.local", ".env.production", ".env"]
    },
    "dev": {
      "dotEnv": [".env.development.local", ".env.local", ".env.development", ".env"]
    },
    "test": {
      "dotEnv": [".env.test.local", ".env.test", ".env"]
    }
  }
}

These fields are ordered lists of Unix-formatted (/-separated) paths, relative to the root for globalDotEnv and relative to the workspace for dotEnv. They do not support globs or absolute paths.

Framework Inference

This feature can be disabled by passing --framework-inference=false to your turbo command.

By default, Turborepo attempts to detect the framework for workspaces inside of your turborepo and uses this to help ensure that all of the default environment variables are properly considered for task hashes.

If Turborepo successfully detects your framework you do not need to manually specify certain framework-specific environment variables inside of turbo.json's pipeline configuration. The supported frameworks and the environment wildcards that Turborepo will include into the task's env key are:

Frameworkenv Wildcard
AstroPUBLIC_*
BlitzNEXT_PUBLIC_*
Create React AppREACT_APP_*
GatsbyGATSBY_*
Next.jsNEXT_PUBLIC_*
Nuxt.jsNUXT_ENV_*
RedwoodJSREDWOOD_ENV_*
Sanity StudioSANITY_STUDIO_*
SolidVITE_*
SvelteKitVITE_*
ViteVITE_*
VueVUE_APP_*

You can determine if Turborepo has successfully detected your framework for a workspace by:

  • Inspecting a run summary (via --summarize).
  • Inspecting the output from a dry run (via --dry).

Framework Inference Exclusions

Turborepo's automatically included wildcards from framework inference may also match environment variables that are inserted into the environment by CI platforms. Those variables can include things like run IDs or Git SHAs which would guarantee never getting a cache hit if included.

As a consequence, Turborepo provides two methods to exclude variables from the hash:

  1. Set TURBO_CI_VENDOR_ENV_KEY with an exclude prefix. This is ideally handled for you by your CI environment if they detect that you are using Turborepo. For example, for a Next.js application building on Vercel, Vercel sets TURBO_CI_VENDOR_ENV_KEY=NEXT_PUBLIC_VERCEL_ to make sure that it doesn't include variables that would result in caching issues. This variable is processed against the inferred framework variables only.

  2. Manually specify the exclusions using "env": ["!NEXT_PUBLIC_UNNEEDED_*"] in the appropriate task definition's env. This enables extremely fine control of environment variable exclusion from hash consideration.

Framework Inference is Per-Workspace

The environment variables will only be included in the cache key for tasks in workspaces where that framework is used. In other words, environment variables inferred for Next.js apps will only be included in the cache key for workspaces detected as Next.js apps. Tasks in other workspaces in the monorepo will not include them.

For example, this turbo.json which specifies build behavior for two separate workspaces (next-app and utils) will only include NEXT_PUBLIC_* for next-app:

{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "next-app#build": {
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "utils#build": {
      "outputs": ["dist/**"],
    },
  }
}

eslint-config-turbo

To further assist in detecting unseen environment variable dependencies creeping into your builds and to help ensure that your Turborepo cache is correctly shared across environments, use the eslint-config-turbo (opens in a new tab) package. This ESLint config will provide authoring-time feedback for usage of environment variables that Turborepo detects as unspecified inside of turbo.json.

To get started, extend from eslint-config-turbo in your eslintrc (opens in a new tab) file:

{
  // Automatically flag env vars missing from turbo.json
  "extends": ["turbo"]
}

For more control over the rules, you can install and configure the eslint-plugin-turbo (opens in a new tab) plugin directly by first adding it to plugins and then configuring the desired rules:

{
  "plugins": ["turbo"],
  "rules": {
    // Automatically flag env vars missing from turbo.json
    "turbo/no-undeclared-env-vars": "error"
  }
}

The plugin will warn you if you are using non-framework-related environment variables in your code that have not been declared in your turbo.json.

Invisible Environment Variables

Since Turborepo runs before your tasks, it is possible for your tasks to create or mutate environment variables after turbo has already calculated the hash for a particular task. For example, consider this package.json:

{
  "scripts": {
    "build": "source .env && next build"
  }
}
export NEXT_PUBLIC_GA_ID=UA-00000000-0

turbo, having calculated a task hash prior to executing the build script, will not discover the NEXT_PUBLIC_GA_ID environment variable's value, and thus be unable to partition the cache based on its value. Be careful to ensure that all of your environment variables are loaded into the environment prior to invoking turbo!

{
  "$schema": "https://turborepo.org/schema.json",
  "pipeline": {
    "build": {
      "env": ["NEXT_PUBLIC_GA_ID"],
      "outputs": [".next/**", "!.next/cache/**"],
    },
  }
}