Environments
One of the main benefits of using environment variables is the ability to boot your application with configuration intended for different environments (e.g., development, preview, staging, production, test).
You can use functions and environment-specific .env
files (e.g., .env.production
) to alter configuration accordingly in a declarative way. Plus the additional guardrails provided by varlock
also make this much safer no matter how values are set.
While many have traditionally shied away from using environment-specific .env
files due to fear of committing sensitive values, the new ability to set values using functions makes it much easier to manage these values in a secure way.
Process overrides
Section titled “Process overrides”varlock
will always treat environment variables passed into the process with the most precedence. This means you can rely on your external hosting platform to inject environment-specific values and still benefit from varlock
’s validation and coercion logic.
However we recommend using injected overrides sparingly, and instead moving more config into your .env
files.
Loading environment-specific .env
files
Section titled “Loading environment-specific .env files”varlock
automatically detects all .env.*
files in the current directory. However any environment-specific files (e.g., .env.development
) will only be loaded if they match the value of the env var set by the @envFlag
root decorator.
The files are applied with a specific precedence (increasing):
.env.schema
- your schema file, which can also contain default values.env
- default values only (not recommended).env.[envFlag]
- environment-specific values.env.local
- local overrides (gitignored).env.[envFlag].local
- environment-specific local overrides (gitignored)
For example, consider the following .env.schema
:
# @envFlag=APP_ENV# ---# @type=enum(development, test, staging, production)APP_ENV=development
Your environment flag key is set to APP_ENV
, which has a default value of development
. By default, .env.development
and .env.development.local
will be loaded if they exist.
To tell varlock
to load .env.staging
instead, you must set APP_ENV
to staging
- usually using an override passed into the process. For example:
APP_ENV=staging varlock run -- node my-test-script.js
Advanced logic using functions
Section titled “Advanced logic using functions”On some platforms, you may not have full control over a build or boot command or the env vars passed into them.
In this case, we can use functions to transform other env vars provided by the platform into the environment flag value we want.
We can use remap()
to transform a value according to a lookup, along with regex()
if we need to match a pattern instead of an exact value.
For example, on the Cloudflare Workers CI platform, we get the current branch name injected as WORKERS_CI_BRANCH
, which we can use to determine which environment to load:
# @envFlag=APP_ENV# ---# set to current branch name when build is running on Cloudflare CI, empty otherwiseWORKERS_CI_BRANCH=# @type=enum(development, preview, production, test)APP_ENV=remap($WORKERS_CI_BRANCH, production="main", preview=regex(.*), development=undefined)
You’ll notice that test
is one of the possible enum values, but it is not listed in the remap.
When running tests, you would just explicitly set APP_ENV
when invoking your command.
APP_ENV=test varlock run -- your-test-command# or if your command is loading varlock internallyAPP_ENV=test your-test-command
or you could run a production style build locally APP_ENV=production varlock run -- your-build-command
Setting a default environment flag
Section titled “Setting a default environment flag”You can set the default environment flag directly when running CLI commands using the --env
flag:
varlock load --env production
This is only useful if you do not want to create a new env var for your env flag, and you are only using varlock via CLI commands. Mostly it is used internally by some integrations to match existing default behavior, and should not be used otherwise.
Using envFlag
in Turborepo
Section titled “Using envFlag in Turborepo”Turborepo users should be aware of a common pitfall when using varlock
’s @envFlag
in monorepos managed by Turborepo, especially since Turborepo v2.0+ now enables Strict Environment Mode by default.
The Problem
Section titled “The Problem”Turborepo, when running tasks, filters the environment variables available to each task. By default in Strict Mode, only variables listed in the env
or globalEnv
keys in your turbo.json
are passed to your scripts. This means that if your @envFlag
(e.g., APP_ENV
) is not explicitly listed, it will not be available to your process, even if you set it in your shell or CI environment. This can cause varlock
to load the wrong environment, or fail to load the correct .env.[envFlag]
file.
Solution: Add your envFlag to turbo.json
Section titled “Solution: Add your envFlag to turbo.json”To ensure your environment flag variable is always available to your scripts, add it to the env
or globalEnv
section of your turbo.json
:
{ "globalEnv": ["APP_ENV"], "tasks": { "build": { "env": ["APP_ENV"] }, "dev": { "env": ["APP_ENV"] } }}
- Use
globalEnv
if the variable should be available to all tasks. - Use
env
under a specific task if only needed for that task.
Now when you run the following:
APP_ENV=production turbo run build
it will load the correct .env.production
file because the override for APP_ENV
is passed correctly to turbo
and in turn to varlock
.
Substitute whatever your env flag is for
APP_ENV
in the above example.
Setting the Environment Flag
Section titled “Setting the Environment Flag”When running locally, or on a platform you control, you can set the env flag explicitly as an environment variable. However on some cloud platforms, there is a lot of magic happening, and the ability to set environment variables per branch is limited. In these cases you can use functions to transform env vars injected by the platform, like a current branch name, into the value you need.
Local/Custom Scripts
Section titled “Local/Custom Scripts”You can set the env var explicitly when you run a command, but often you will set it in package.json
scripts:
"scripts": { "build:preview": "APP_ENV=preview next build", "start:preview": "APP_ENV=preview next start", "build:prod": "APP_ENV=production next build", "start:prod": "APP_ENV=production next start", "test": "APP_ENV=test jest"}
Vercel
Section titled “Vercel”You can use the injected VERCEL_ENV
variable to match their concept of environment types:
# @envFlag=APP_ENV# ---# @type=enum(development, preview, production)VERCEL_ENV=# @type=enum(development, preview, production, test)APP_ENV=fallback($VERCEL_ENV, development)
For more granular environments, use the branch name in VERCEL_GIT_COMMIT_REF
(see Cloudflare example below).
Cloudflare Workers Build
Section titled “Cloudflare Workers Build”Use the branch name in WORKERS_CI_BRANCH
to determine the environment:
# @envFlag=APP_ENV# ---WORKERS_CI_BRANCH=# @type=enum(development, preview, production, test)APP_ENV=remap($WORKERS_CI_BRANCH, production="main", preview=regex(.*), development=undefined)