Skip to content
Built with

Environment variables ​

Build & Deploy

Declare your environment variables in env.ts, then read them through env from void/env. Use .env for local values and void secret put for production values. Never commit real secrets.

On this pageDefine a typed env schema · Set local values · Store production secrets safely

Define the schema ​

This step creates env.ts at the project root.

ts
import { defineEnv, number, string, url } from 'void/env';

export default defineEnv({
  DATABASE_URL: url(),
  STRIPE_KEY: string(),
  PORT: number().default(3000),
  VITE_API_ORIGIN: url(),
});

This step imports the typed env object in your app.

ts
import { env } from 'void/env';

const databaseUrl = env.DATABASE_URL;

The built-in helpers are string(), number(), boolean(), url(), email(), oneOf([...]), and json<T>(). They support .optional() and .default(value). Any Standard Schema validator also works.

Defaults are source-code constants. Use them for non-sensitive fallbacks. Never put credentials in a default.

Local development ​

This step puts local values in one root .env file.

ini
DATABASE_URL=postgres://localhost/my-app
STRIPE_KEY=sk_test_example
VITE_API_ORIGIN=http://localhost:5173

An unquoted value ends at #, which starts a comment. Quote a value that contains #, such as DATABASE_URL="postgres://user:p#ss@localhost/my-app".

Keep .env out of Git. Other dotenv files, including .env.local, .env.production, .env.example, and Wrangler’s .dev.vars*, are not supported.

Names starting with __VOID_ are reserved for the SDK. The SDK stops with an error if .env or env.ts defines one.

This step checks your local values.

sh
void env check

Example output: A pass, or a list of missing or invalid variables.

Local database, migration, auth and dev-server tools all read the same .env. Shell values override it.

Production server values ​

Requires: login or token

Secret values must stay out of code, chat and commits.

Every key without a client prefix is a server value. This step sets server values remotely.

sh
void secret put DATABASE_URL
void secret put STRIPE_KEY

Example output: A prompt for each secret value. The values are not printed.

This step uploads in bulk. Use a dotenv-format file with only production server values.

sh
void secret sync production-secrets.txt

Example output: A confirmation of the secrets synced from the file.

Secret commands validate values against env.ts before upload. Production values are encrypted and validated again at Worker startup.

Client-prefixed keys are rejected by secret commands. Keep local development credentials and VITE_* values out of the production secrets file.

void deploy requires every required server key to exist remotely. It never uploads .env values.

The same secret commands work for direct Cloudflare deployments. Schema-declared server values must be Worker secrets, not plaintext vars.

Client values ​

Keys beginning with VITE_ are client values, even if your framework customizes Vite's envPrefix.

Client values ship in browser JavaScript and are never secret. This step passes one through the build shell. A schema default also works.

sh
VITE_API_ORIGIN=https://api.example.com void deploy

Example output: The build uses the given public value, then deploys.

ts
export default defineEnv({
  VITE_API_ORIGIN: url().default('https://api.example.com'),
});

Builds ignore client values from .env. A local value can never silently become production config.

Server rendering uses the same public build values as the browser.

Validation and redaction ​

sh
void env check           # validate .env + shell for local development
void env check --remote  # validate build-time client values and remote server names

Example output: A validation result for local values, then for remote values.

sh
void env types           # regenerate .void/env.d.ts

Example output: A regenerated .void/env.d.ts file.

Invalid-value diagnostics redact the supplied value by default. Set VOID_ENV_UNMASK=1 only for deliberate local debugging. The SDK prints a notice when redaction is off.

Imports inside env.ts ​

Use relative imports or package names inside env.ts. TypeScript path aliases and imports that need Vite transforms are not supported.

Next steps ​

implemented.stream AI