Environment variables

Keep your project's values in Doppler instead of sending .env files around. The repository lists the names, sixb env pull writes your .env, and your app reads process.env as it always has.

List what the project needs

sixb.env.ts holds names only, so commit it:

TS
import { defineEnv } from "@sixb/core/env"
import { doppler } from "@sixb/env-doppler"

export default defineEnv({
  source: doppler({ project: "client-a" }),
  vars: {
    DATABASE_URL: "required",
    LINKEDIN_CLIENT_SECRET: "prod",
    SENTRY_DSN: "optional",
  },
})
RuleThe variable must have a value
requiredEverywhere
prodIn prod, and when NODE_ENV is production
optionalNever. The name is only recorded

sixb dev and sixb check stop before starting when a variable has no value, and an empty value counts as none. In a terminal, sixb dev offers to pull:

TXT
Missing RESEND_API_KEY, which sixb.env.ts requires.
Pull from dev now? (Y/n)

source is optional. Without it you keep your own .env, and still get these checks. A project without sixb.env.ts runs without them.

Move a project over

Create the project in Doppler, install the package and the Doppler CLI, then upload your current files:

BASH
bun add @sixb/env-doppler
bun sixb env init client-a --dev .env --prod .env.prod

This uploads the values and writes sixb.env.ts with a first guess at each rule. Check the rules, commit the file, and delete .env.prod. It stops without changing anything if Doppler already has a different value for a name.

Work with the values

TaskHow
Start on a projectbun sixb env pull. The first time, it signs you in to Doppler
Get a teammate's changebun sixb env pull
Add or change a valuebun sixb env set <NAME> --env <dev|prod>, or Doppler's dashboard
See what's setbun sixb env status, which never shows values
Add or remove a teammateDoppler's dashboard. When someone leaves, rotate what they could read

sixb env set asks for the value with a hidden prompt, or reads it from standard input, so it never lands in your shell's history.

sixb env status reads names only, so it shows a value that was blanked in Doppler as set. A pull and a deploy still stop on it.

Doppler accepts uppercase names only, and reads ${NAME} in a value as a reference to another variable, which it resolves before .env is written. It refuses a reference it can't resolve, along with any other name or value it won't take, and says why.

Your .env

sixb env pull replaces .env, so put your own overrides in .env.local, which takes priority. If you wrote the current .env by hand and it has values Doppler doesn't, the pull stops and names them rather than losing them. Pass --force to replace it anyway.

A pull stops, leaving .env as it was, when a variable sixb.env.ts requires isn't in Doppler. It also stops, naming the variable, for a value a .env file can't hold: one with a carriage return, one ending in a backslash, or one using all of ', " and `.

Production values

sixb env pull writes dev, and refuses prod on your computer so production values stay on your servers. To read one, use Doppler's dashboard. Deploying doesn't need your computer to hold them: the server pulls for itself.

Deploy

With source set, each deploy to a server writes that server's .env from prod, so there's nothing to edit on the server. Run setup once, from a computer signed in to Doppler:

BASH
bun sixb deploy setup

It creates a read-only token that can read this project's prd config and nothing else, and installs it on the server at ~/<name>/env-token. To replace it, revoke it in Doppler, delete that file, and run setup again.

A deploy then pulls before it builds. Because that's before the services stop, a pull that fails leaves your running release alone:

What happensThe deploy
sixb.env.ts requires a variable prod doesn't haveStops and names it
Doppler refuses the tokenStops
Doppler can't be reachedContinues on the current .env with a warning, if it was generated by a pull and has every required variable. Otherwise stops
The server's .env was written by hand, with values prod doesn't matchStops and names them. Move what you want to keep to Doppler, then delete the file

Your app reads only .env, so a running service or a restart never depends on Doppler. Changing a value in Doppler reaches the server at the next deploy. Rolling back with --ref keeps the current values, so a key you revoked doesn't come back.

Anyone who can sign in to the server can read .env and the token, and so can a job holding the deploy key. Limit server access to people you'd trust with production.

Tests in CI

Give a test job that needs values a read-only token for dev, created in Doppler:

YAML
- env:
    DOPPLER_TOKEN: ${{ secrets.DOPPLER_DEV_TOKEN }}
  run: bun sixb env pull

Search docs

Search the documentation