The nineteen names
Why half of the configuration is a file and half of it is somewhere else, and how the panel reports both.
The installation has nineteen settings that are read from the environment rather than stored in the database. Database settings are edited in the panel and change instantly. Environment settings are read when a process starts, which is what makes them the right place for keys and addresses, and what makes them the wrong place for anything you change often.
The two homes
The app
Six names in .env.local, read by the Next.js process on the machine it runs on. Anything in app/api/.
The deployment
Twelve names in the Convex deployment's environment, read by the functions where they run. Anything in convex/.
That split is not a preference, it is where the code runs. The route that seals a token needs the key that seals it, and that route is in the app. The function that signs a session needs the key that signs it, and that function is on the deployment. A value put in the wrong one is present, looks right, and does nothing.
The failure this prevents
An installation once reported SETTINGS_SECRET as missing while the vault was
sealing tokens with it, because the screen was checking the deployment for a
name that only the app reads. The panel now says which home it is checking,
and it checks both.
Where you set them
Which of them are optional
Two of the nineteen are genuinely optional, and the panel counts them separately so that "unset" does not look like "broken".
| Name | When it is fine to leave unset |
|---|---|
ALLOW_SELF_UPDATE | Always, unless you want the panel to update this installation by itself. It must be exactly 1 |
CREDIT_MONTHLY_CAP | Always. The Credits section has its own ceiling, and the shipped number is used when this is unset |
Everything else is either required to start or switches off the feature that needs it. Nothing else breaks the app: Google sign in without Google's two names is a button that says so when it is pressed.
The order that works
The database address first
NEXT_PUBLIC_CONVEX_URL is the one name the app cannot serve without. npx convex dev creates or
connects a deployment and prints it. See Convex, set up properly.
The two secrets
SETTINGS_SECRET for the app and BETTER_AUTH_SECRET for the deployment. Two long random strings, and
the second one signing everybody out when it changes:
openssl rand -base64 32 # run it twice, once for eachThe addresses
NEXT_PUBLIC_APP_URL and SITE_URL, which are the same address in two places. Every callback, every
email link and every provider redirect is built from them, so they have to be the address people actually
visit. See Addresses and secrets.
The sign-in providers, if you want more than a password
Google and GitHub, each a client ID and a secret. Google sign in and GitHub sign in walk through the consoles screen by screen.
Mail, then the first administrator
RESEND_API_KEY and EMAIL_FROM for the codes, then ADMIN_EMAIL, ADMIN_PASSWORD and
ADMIN_NAME. See Email that arrives.
Rotating one
| Name | What happens when it changes |
|---|---|
SETTINGS_SECRET | Every sealed token becomes unreadable. Reconnect integrations afterwards |
BETTER_AUTH_SECRET | Everybody is signed out. Accounts and data are untouched |
| An OAuth client secret | Sign in through that provider stops until the new secret is in |
NEXT_PUBLIC_CONVEX_URL | The app talks to a different backend. Change it and nothing else |
NEXT_PUBLIC_APP_URL or SITE_URL | Callbacks and email links change. Update the providers to match |
After changing any of them, restart the app. The panel says so on the screens where it matters, and
System → Status & health is where you confirm the result.

