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*"
: matchesFOO
,FOOD
,FOO_FIGHTERS
, etc."FOO\*"
: because this is specified in JSON, this resolves to"FOO*"
and matchesFOO
,FOOD
, andFOO_FIGHTERS
."FOO\\*"
: matches a single environment variable namedFOO*
."!FOO*"
: excludes all environment variables that start withFOO
."\!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 namedFOO!
.
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:
Framework | env Wildcard |
---|---|
Astro | PUBLIC_* |
Blitz | NEXT_PUBLIC_* |
Create React App | REACT_APP_* |
Gatsby | GATSBY_* |
Next.js | NEXT_PUBLIC_* |
Nuxt.js | NUXT_ENV_* |
RedwoodJS | REDWOOD_ENV_* |
Sanity Studio | SANITY_STUDIO_* |
Solid | VITE_* |
SvelteKit | VITE_* |
Vite | VITE_* |
Vue | VUE_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:
-
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 setsTURBO_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. -
Manually specify the exclusions using
"env": ["!NEXT_PUBLIC_UNNEEDED_*"]
in the appropriatetask
definition'senv
. 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/**"],
},
}
}