Skip to content

Encrypted deployments

When deploying SSR applications to serverless platforms (like Vercel, Netlify, or Cloudflare Workers), varlock injects the fully resolved env data into your server-side build output so it’s available at runtime without needing the CLI or filesystem access. This is necessary because serverless environments don’t give you control over how the application boots.

By default, this blob is plaintext JSON. Since it only appears in server-side code, it’s generally safe — your secrets are not exposed to the client. However, encrypting the blob provides an extra layer of protection, particularly against secrets leaking via sourcemaps that may be uploaded to error tracking services.

Add the @encryptInjectedEnv root decorator to your .env.schema:

.env.schema
# @encryptInjectedEnv
# ---
SECRET_KEY= # @sensitive

You can also enable it conditionally for specific environments:

.env.schema
# @encryptInjectedEnv=forEnv(prod)
# ---
SECRET_KEY= # @sensitive
  1. At build time, varlock encrypts the serialized env graph with AES-256-GCM before injecting it
  2. The encrypted blob is prefixed with varlock:v1: — you can verify encryption by inspecting your build output
  3. At runtime, initVarlockEnv() detects the encrypted prefix and decrypts using _VARLOCK_ENV_KEY from the runtime environment
  4. The key is never baked into the build — it must always come from the runtime environment

The encryption key (_VARLOCK_ENV_KEY) is managed differently depending on your platform:

No setup needed — varlock auto-generates a temporary key when running dev servers. The key exists only in memory for the duration of the dev session.

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.

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

  1. Generate and set the key

    Terminal window
    # set one key for preview
    varlock generate-key --plain | vercel env add _VARLOCK_ENV_KEY preview --sensitive
    # set one key for production
    varlock generate-key --plain | vercel env add _VARLOCK_ENV_KEY production --sensitive

    You can use the same key for both preview and production, but using separate keys is usually safer because it isolates environments and reduces blast radius if one key is exposed.

  2. Deploy your app

    The build will fail with a clear error if @encryptInjectedEnv is enabled but _VARLOCK_ENV_KEY is not set.

IntegrationHow it works
Next.jsEncrypts the env blob injected into the webpack/turbopack build output
ViteEncrypts the env blob when using ssrInjectMode: 'resolved-env'
AstroUses the Vite integration — encryption applies to SSR adapter builds
SvelteKitUses the Vite integration — encryption applies to SSR builds
TanStack StartUses the Vite integration — encryption applies to SSR builds
Cloudflare WorkersEncrypts the __VARLOCK_ENV secret binding uploaded at deploy time
ExpoEncrypts the env blob used by the Metro bundler for server routes

After building with encryption enabled, you can inspect your build output to confirm the blob is encrypted:

Terminal window
# Look for the encrypted prefix in build output
grep -r 'varlock:v1:' dist/ .next/server/ 2>/dev/null

If 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 simply 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.