---
title: Environment Variable Inputs
description: How to configure Turborepo environment variables
---

import { Callout } from "../../../../../components/Callout";

# 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.

```jsonc filename="/turbo.json"
{
  "$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:

```jsonc filename="/turbo.json"
{
  "$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.

<Callout>
  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](/repo/docs/reference/command-line-reference/run#--summarize) from all of your environments in order to make sure that it is only including variables you care about.
</Callout>

### Syntax

<Callout>
  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.
</Callout>

- 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](#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](#hashed-environment-variables).

If you **do not** want an environment variable to be part of the cache key, use [Unhashed Environment Variables](#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

<Callout>
  Turborepo does not load `.env` files into the environment! Your task must handle loading of the `.env` files itself.
</Callout>

Frameworks commonly use [`dotenv`](https://github.com/motdotla/dotenv) 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:

<Callout type="info">
  In most cases, this configuration should be matched to your framework's behavior, not the particular configuration you're presently using for your application.
</Callout>

```jsonc filename="/turbo.json"
{
  "$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

<Callout type="info">
  This feature can be disabled by passing [`--framework-inference=false`](/repo/docs/reference/command-line-reference/run#--framework-inference) to your `turbo` command.
</Callout>

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`](/repo/docs/reference/command-line-reference/run#--summarize)).
- Inspecting the output from a dry run (via [`--dry`](/repo/docs/reference/command-line-reference/run#--dry----dry-run)).

### 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`:

```jsonc filename="/turbo.json"
{
  "$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`](https://www.npmjs.com/package/eslint-config-turbo) 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`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#configuration-file-formats) file:

```jsonc filename=".eslintrc.json"
{
  // 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`](https://www.npmjs.com/package/eslint-plugin-turbo) _plugin_ directly by first adding it to plugins and then configuring the desired rules:

```jsonc filename=".eslintrc.json"
{
  "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`:

```json filename="package.json"
{
  "scripts": {
    "build": "source .env && next build"
  }
}
```

```sh filename=".env"
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`!

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