Encrypted deployments
When deploying SSR applications to serverless platforms (like Vercel or Netlify) that don’t give you control over how the application boots, varlock bakes the fully resolved env into your server-side build output so it’s available at runtime without the CLI or filesystem access: the Next.js integration freezes it into the build, and the Vite-based integrations inject it with ssrInjectMode: 'resolved-env'. Cloudflare Workers is different: varlock-wrangler deploy uploads the blob as a runtime secret binding instead of baking it into the build output, so it never appears in a build artifact or sourcemap in the first place. See the Cloudflare Workers section below.
Encrypting the blob protects your secrets if the build output is exposed, for example via sourcemaps uploaded to an error tracking service.
- Next.js always encrypts an env it freezes into the build, so all you need to do is set the key. The build fails until you do (or until you opt out with
_VARLOCK_ALLOW_PLAINTEXT_FROZEN_ENV=1). - Vite-based integrations inject a plaintext blob by default. Enable encryption with the decorator below, then set the key.
Enabling encryption
Section titled “Enabling encryption”For the Vite-based integrations, add the @encryptInjectedEnv root decorator to your .env.schema:
# @encryptInjectedEnv# ---SECRET_KEY= # @sensitiveYou can also enable it conditionally for specific environments:
# @encryptInjectedEnv=forEnv(prod)# ---SECRET_KEY= # @sensitiveHow it works
Section titled “How it works”- At build time, varlock encrypts the serialized env graph with AES-256-GCM before injecting it
- The encrypted blob is prefixed with
varlock:v1:, so you can verify encryption by inspecting your build output - At runtime,
initVarlockEnv()detects the encrypted prefix and decrypts using_VARLOCK_ENV_KEYfrom the runtime environment - The key is never baked into the build. It must always come from the runtime environment
Decryption uses node:crypto where available; edge runtimes without it (e.g. Vercel Edge middleware) automatically fall back to asynchronous Web Crypto, with request handling gated until the env is ready.
Key management
Section titled “Key management”The encryption key (_VARLOCK_ENV_KEY) is managed differently depending on your platform:
Local development
Section titled “Local development”Nothing to set up. When @encryptInjectedEnv is enabled, the Vite plugin mints a temporary key in an early dev-server config hook. Frameworks that run the SSR dev runtime in a separate worker or process (Nitro and TanStack Start, Nuxt, vitest) spawn it later with a copy of the environment, so they inherit the key and the blob is encrypted in dev the same way it is in production. The key lives only in memory for that dev session and is never created for builds. If you export _VARLOCK_ENV_KEY yourself before starting the dev server, that key is used instead. vite preview never mints a key: it serves a finished build, so it needs the same real key the build used.
On Cloudflare targets, the dev blob is always plaintext: the workerd dev runtime reads process.env from bindings, so no host env var can reach it. Dev output is never deployed, so this is safe. If you use another runtime that cannot see host env vars, scope the decorator to production with @encryptInjectedEnv=forEnv(prod).
Cloudflare Workers
Section titled “Cloudflare Workers”No setup needed. varlock-wrangler deploy auto-generates a key and uploads it alongside your encrypted env as a Cloudflare secret binding. The key persists across deploys.
Vercel, Netlify, and other platforms
Section titled “Vercel, Netlify, and other platforms”You must set _VARLOCK_ENV_KEY as an environment variable on your platform. The key must be available at both build time (for encryption) and runtime (for decryption). It must be a real environment variable in both places: it’s a reserved _VARLOCK_* name that varlock never populates from .env files, and it can’t be baked in via vite’s define (the value has to come from the actual process environment at runtime, not a string substituted at build time).
-
Generate and set the key
Terminal window # set one key for previewvarlock generate-key --plain | vercel env add _VARLOCK_ENV_KEY preview --sensitive# set one key for productionvarlock generate-key --plain | vercel env add _VARLOCK_ENV_KEY production --sensitiveYou can use the same key for both
previewandproduction, but using separate keys is usually safer because it isolates environments and reduces blast radius if one key is exposed.Set it as a project environment variable like this (or in the dashboard). Vercel env vars apply to both builds and runtime by default. Don’t pass it with
vercel deploy --envor--build-env: those reject names that start with_.Terminal window netlify env:set _VARLOCK_ENV_KEY $(varlock generate-key --plain) --secretGenerate a key and set it as a secret/env var on your platform:
Terminal window npm exec -- varlock generate-keyTerminal window pnpm exec -- varlock generate-keyTerminal window bunx varlock generate-keyTerminal window vlx -- varlock generate-keyTerminal window yarn exec -- varlock generate-keyBuilding a Docker image yourself? Pass the key to the build as a build secret so it never lands in an image layer, and to the container at runtime. See Next.js standalone images.
-
Deploy your app
The build fails with a clear error if
_VARLOCK_ENV_KEYis not set when encryption is required: a Next.js build that freezes the env, or@encryptInjectedEnvwith a Vite-based integration. If the key is missing at runtime, the server fails at boot rather than serving.
Supported integrations
Section titled “Supported integrations”| Integration | How it works |
|---|---|
| Next.js | Encrypts the env frozen into the build output. Always on: a frozen build fails without _VARLOCK_ENV_KEY, with or without the decorator |
| Vite | Encrypts the env blob when using ssrInjectMode: 'resolved-env' |
| Astro | Uses the Vite integration; encryption applies to SSR adapter builds |
| SvelteKit | Uses the Vite integration; encryption applies to SSR builds |
| TanStack Start | Uses the Vite integration; encryption applies to SSR builds |
| Cloudflare Workers | Encrypts the __VARLOCK_ENV secret binding uploaded at deploy time |
| Expo | Encrypts the env blob used by the Metro bundler for server routes |
Verifying encryption
Section titled “Verifying encryption”After building with encryption enabled, you can inspect your build output to confirm the blob is encrypted:
# Look for the encrypted prefix in build outputgrep -r 'varlock:v1:' dist/ .next/server/ 2>/dev/nullIf encryption is working, you’ll see varlock:v1: followed by a base64-encoded ciphertext instead of raw JSON.
Alternative: manual key without the decorator
Section titled “Alternative: manual key without the decorator”You can also enable encryption without the @encryptInjectedEnv decorator by setting _VARLOCK_ENV_KEY in your environment. When present, varlock will encrypt the blob, but it won’t enforce the key’s presence or auto-generate it.