Skip to content

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.

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.

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:

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

Terminal window
APP_ENV=staging varlock run -- node my-test-script.js

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:

.env.schema
# @envFlag=APP_ENV
# ---
# set to current branch name when build is running on Cloudflare CI, empty otherwise
WORKERS_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.

Terminal window
APP_ENV=test varlock run -- your-test-command
# or if your command is loading varlock internally
APP_ENV=test your-test-command

or you could run a production style build locally APP_ENV=production varlock run -- your-build-command

You can set the default environment flag directly when running CLI commands using the --env flag:

Terminal window
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.