What refuses boot
Every check that stops either half from starting, or from serving, and the code that runs it. Each message names the variables at fault.
Backend
These run at startup, from backend/src/main.ts or while Nest builds the
app. A failed check throws and the process exits.
Secrets, in production only. validateSecrets checks AUTH_SECRET,
ADMIN_MCP_API_KEY and INTERNAL_API_TOKEN. With NODE_ENV=production
it refuses one that is unset, shorter than 32 characters, or still a
placeholder: starting with generate- or docker-dev-, or ending with
-change-in-production. In development it prints the same list as a
warning and starts. Generate values with openssl rand -base64 32.
In every environment:
DATABASE_URLunset, inbackend/src/knex/knex.module.ts.- Billing,
validateBillingEnvinbackend/src/billing/billing.config.ts:STRIPE_WEBHOOK_SECRET,STRIPE_PRO_PRICE_IDorSTRIPE_AUTOMATIC_TAXset withoutSTRIPE_SECRET_KEY; aSTRIPE_SECRET_KEYthat does not start withsk_orrk_;STRIPE_SECRET_KEYwithoutSTRIPE_WEBHOOK_SECRET.STRIPE_PRO_PRICE_IDitself is not required. USER_DELETION_MAX_ATTEMPTSset to anything but a positive integer.CLIENT_IP_HEADERset to something that is not a header name.TRUST_PROXYset to anything buttrue,falseor a non-negative integer. An address or subnet is refused. Unset or empty in production only prints a warning.SMTP_FROMunset, orSMTP_PORTset to anything but an integer from 1 to 65535, checked whenEmailServiceis constructed. Unset or emptySMTP_PORTis 1025.
"Integer" here means digits only: 5, not 5, 1.5, 5junk or 1e1.
Frontend
Exits at startup, in production only. With NODE_ENV=production, and
never during next build, instrumentation.ts runs
collectRuntimeEnvProblems from frontend/lib/required-runtime-env.ts
and exits the process on any problem:
AUTH_URLorINTERNAL_API_TOKENunset;EMAIL_SERVER_PORT,EMAIL_SERVER_USER,EMAIL_SERVER_PASSWORDorEMAIL_FROMset withoutEMAIL_SERVER_HOST;EMAIL_SERVER_HOSTset withoutEMAIL_FROM, or without anEMAIL_SERVER_PORTfrom 1 to 65535. User and password stay optional;- a
SERVER_FETCH_TIMEOUT_MSthatNumber()does not read as a whole number from 0 to 2147483647. In developmentnext devexits on it too.
Fails requests, does not exit, in production only. auth.ts runs the
same check when it is first imported, and also requires
NEXT_PUBLIC_SITE_URL and NEXT_PUBLIC_API_URL. Next inlines those two
into the bundle at next build, so only the built code can check the
values that shipped; set them as build args. Next turns the throw into a
500 on the request that imports auth.ts, and keeps serving every other
route, including a /robots.txt health probe.