Unstack Pro Docs

Environment Variables

Complete guide to configuring Unstack Pro environment variables

Environment Variables

Unstack Pro reads configuration from two separate places:

  1. The Next.js environment.env.local in local development, or your host's environment variables (e.g. Vercel) in production.
  2. The Convex deployment environment — set with bunx convex env set NAME "value" or from the Convex dashboard → Settings → Environment Variables.

Some variables live in only one of these; a few must be set in both. Each variable below is labelled with the environment(s) it belongs to. The .env.example file in the repo is split into the same two sections.

Security Notice: Never commit your .env.local file to version control. Keep your secrets secure!

Quick Setup

Copy the example file and fill in your values:

cp .env.example .env.local

Then set the Convex-side variables with the CLI (with bun convex dev running):

bunx convex env set NAME "value"

Section 1 — Next.js environment (.env.local)

These are read by the Next.js app. NEXT_PUBLIC_* values are also inlined into the browser bundle. In production, set the same values in your host's environment variables (see Deployment).

NEXT_PUBLIC_CONVEX_URL

Required: Yes · Environment: Next.js

Your Convex cloud deployment URL (….convex.cloud), printed by bun convex dev.

NEXT_PUBLIC_CONVEX_URL="https://your-deployment.convex.cloud"

NEXT_PUBLIC_CONVEX_SITE_URL

Required: Yes · Environment: Next.js

The Convex HTTP Actions URL — the same deployment on the .convex.site domain. Take NEXT_PUBLIC_CONVEX_URL and swap .convex.cloud for .convex.site. Required by the auth proxy in lib/auth-server.ts.

NEXT_PUBLIC_CONVEX_SITE_URL="https://your-deployment.convex.site"

NEXT_PUBLIC_APP_URL

Required: Yes · Environment: Next.js and Convex

The public URL of this Next.js app. Used for SEO metadata, the sitemap, email links, and the passkey origin. Use http://localhost:3000 for local dev.

NEXT_PUBLIC_APP_URL="http://localhost:3000"

This value is also needed on the Convex deployment (Convex-side email links and the passkey origin use it). Set it in both places.

NEXT_PUBLIC_APP_NAME

Required: Yes · Environment: Next.js and Convex

Display name shown throughout the UI and transactional emails. Email templates run on Convex, so set this in both environments.

NEXT_PUBLIC_APP_NAME="Unstack Pro"

NEXT_PUBLIC_SUPPORT_EMAIL

Required: No · Environment: Next.js

Support address surfaced in the landing page / FAQ copy. Falls back to a placeholder if unset.

NEXT_PUBLIC_SUPPORT_EMAIL="support@yourcompany.com"

AUTUMN_SECRET_KEY

Required: Yes (for billing) · Environment: Next.js and Convex

Your Autumn billing secret key (starts with am_sk_).

AUTUMN_SECRET_KEY must be set in both environments. The Next.js billing routes (app/api/autumn/[...all]/route.ts) read it at runtime in the Node runtime, and the Convex auth hooks read it on the deployment. Setting it in only one place will break checkout/portal or seat syncing.

AUTUMN_SECRET_KEY="am_sk_..."

Sentry (optional)

Required: No · Environment: Next.js

Error monitoring. Leave all of these unset to disable Sentry entirely — the SDK no-ops when NEXT_PUBLIC_SENTRY_DSN is missing, and builds skip source-map upload when SENTRY_AUTH_TOKEN is missing.

NEXT_PUBLIC_SENTRY_DSN=""
SENTRY_ORG=""
SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""
VariablePurpose
NEXT_PUBLIC_SENTRY_DSNSentry project DSN. When empty, Sentry is disabled.
SENTRY_ORGSentry org slug (build-time, for source maps).
SENTRY_PROJECTSentry project slug (build-time, for source maps).
SENTRY_AUTH_TOKENToken used to upload source maps at build time.

CONVEX_DEPLOYMENT is written into .env.local automatically by bun convex dev — you don't set it by hand. It identifies which deployment the CLI talks to.


Section 2 — Convex deployment environment

These live on the Convex deployment, not in .env.local. Set each with bunx convex env set NAME "value" (or via the Convex dashboard → Settings → Environment Variables).

BETTER_AUTH_SECRET

Required: Yes · Environment: Convex

Random secret used by Better Auth for session/token encryption. The JWKS private key is encrypted with it, so set this before generating JWKS.

Generate with:

openssl rand -base64 32

JWKS

Required: Yes · Environment: Convex

The RS256 signing key set for Convex JWTs. convex/auth.config.ts reads the public key set from this variable; Better Auth signs tokens with the stored private key.

The stack ships a Convex action that generates the key pair if none exists and returns it in the exact shape the auth config expects. With bun convex dev running (so the functions are deployed):

# Prints the JWKS document set.
bunx convex run betterAuth/auth:getLatestJwks

Set the output as the JWKS env var. On macOS/Linux you can do it in one step:

bunx convex env set JWKS "$(bunx convex run betterAuth/auth:getLatestJwks)"

On Windows, copy the printed JSON and paste it into bunx convex env set JWKS '…'.

To rotate keys, run bunx convex run betterAuth/auth:rotateKeys and set the returned value as JWKS again. Key rotation logs out all users — do it only when necessary, during low-traffic periods.

BETTER_AUTH_URL

Required: No (kept for the CLI) · Environment: Convex

Unused at runtime — kept only for the Better Auth CLI. The runtime uses NEXT_PUBLIC_APP_URL instead, so you don't normally need to touch this.

BETTER_AUTH_URL="http://localhost:3000"

EMAIL_FROM

Required: Yes · Environment: Convex

A sender address verified in Resend. Every email flow throws if this is missing.

EMAIL_FROM="Unstack Pro <noreply@yourdomain.com>"

Format: "Display Name <email@domain.com>".

RESEND_API_KEY

Required: Yes · Environment: Convex

Your Resend API key for transactional email (verification, OTP, invitations).

RESEND_API_KEY="re_..."

AUTUMN_SECRET_KEY

Required: Yes (for billing) · Environment: Convex and Next.js

Same key as in Section 1 — the Convex auth hooks read it for seat syncing and the Pro-plan gate. See the callout under Section 1: it must be set in both environments.

PASSKEY_RP_ID

Required: Yes · Environment: Convex

The Relying Party ID for WebAuthn/passkeys — the bare public domain, no scheme or port. Use localhost for local dev; in production it must exactly match your deployed domain.

PASSKEY_RP_ID="localhost"

For production with subdomains, use the root domain (e.g. example.com, not app.example.com) so passkeys work across all subdomains. NEXT_PUBLIC_APP_URL must be the matching https:// origin.

NEXT_PUBLIC_APP_URL / NEXT_PUBLIC_APP_NAME

Also set these on the Convex deployment (same values as .env.local) — Convex-side email links and the passkey origin use NEXT_PUBLIC_APP_URL, and email templates use NEXT_PUBLIC_APP_NAME.


Setting Convex environment variables

# Auth
bunx convex env set BETTER_AUTH_SECRET "$(openssl rand -base64 32)"
bunx convex env set JWKS "$(bunx convex run betterAuth/auth:getLatestJwks)"

# Email
bunx convex env set RESEND_API_KEY "re_..."
bunx convex env set EMAIL_FROM "Unstack Pro <noreply@yourdomain.com>"

# Billing (also required in .env.local)
bunx convex env set AUTUMN_SECRET_KEY "am_sk_..."

# Application (same values as .env.local)
bunx convex env set NEXT_PUBLIC_APP_URL "http://localhost:3000"
bunx convex env set NEXT_PUBLIC_APP_NAME "Unstack Pro"

# Passkeys
bunx convex env set PASSKEY_RP_ID "localhost"

You can view and manage Convex environment variables in your Convex dashboard.

Complete .env.local example

.env.local
# --- Section 1: Next.js environment ---
NEXT_PUBLIC_CONVEX_URL="https://your-deployment.convex.cloud"
NEXT_PUBLIC_CONVEX_SITE_URL="https://your-deployment.convex.site"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_APP_NAME="Unstack Pro"
NEXT_PUBLIC_SUPPORT_EMAIL="support@yourcompany.com"

# Required in BOTH environments (also set on Convex)
AUTUMN_SECRET_KEY="am_sk_..."

# Optional — Sentry (leave empty to disable)
NEXT_PUBLIC_SENTRY_DSN=""
SENTRY_ORG=""
SENTRY_PROJECT=""
SENTRY_AUTH_TOKEN=""

The Convex-side variables (BETTER_AUTH_SECRET, JWKS, EMAIL_FROM, RESEND_API_KEY, AUTUMN_SECRET_KEY, PASSKEY_RP_ID, NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_APP_NAME) are set with bunx convex env set, not in .env.local.

Environment Variable Checklist

Before deploying, make sure these are set:

Next.js environment (.env.local / host):

  • NEXT_PUBLIC_CONVEX_URL
  • NEXT_PUBLIC_CONVEX_SITE_URL
  • NEXT_PUBLIC_APP_URL
  • NEXT_PUBLIC_APP_NAME
  • AUTUMN_SECRET_KEY
  • NEXT_PUBLIC_SUPPORT_EMAIL (optional)
  • Sentry vars (optional)

Convex deployment:

  • BETTER_AUTH_SECRET
  • JWKS
  • EMAIL_FROM (verified Resend sender)
  • RESEND_API_KEY
  • AUTUMN_SECRET_KEY
  • PASSKEY_RP_ID
  • NEXT_PUBLIC_APP_URL and NEXT_PUBLIC_APP_NAME

Troubleshooting

"X is not defined" errors

When Convex code throws that a variable is missing, consult createAuthOptions in convex/betterAuth/auth.ts for the full required set, and confirm the variable is set on the Convex deployment (not just .env.local).

Billing routes 500 or seats don't sync

AUTUMN_SECRET_KEY is missing from one of the two environments. Set it in both .env.local (Next.js runtime) and on the Convex deployment.

Passkeys not working

PASSKEY_RP_ID must match your domain exactly (localhost for dev, the root domain in production), and NEXT_PUBLIC_APP_URL must be the matching origin.

Security Best Practices

  1. Never commit secrets: keep .env.local in .gitignore.
  2. Rotate JWKS periodically: bunx convex run betterAuth/auth:rotateKeys, then set the output as JWKS (this logs everyone out).
  3. Use different secrets for development and production.
  4. Restrict API keys to the minimum permissions necessary.
  5. Monitor access: review Convex and Resend logs regularly.

Production Reminder: Always use HTTPS in production. Never use http:// URLs for NEXT_PUBLIC_APP_URL.

On this page