Environment Variables
UI environment config lives in:
apps/ui/.env.local
apps/ui/src/env.mjs
apps/ui/src/lib/env-vars.ts
All variables are optional in src/env.mjs: the schema uses @t3-oss/env-nextjs and intentionally allows building without secrets baked in. Runtime code must still check that required values exist where they are used.
Do not leave empty values such as DATABASE_PASSWORD= in .env. Empty values are treated as set, so default config fallbacks will not be used. Remove or comment the line when you want the default.
getEnvVar()Use getEnvVar() from src/lib/env-vars.ts instead of reading process.env directly. It works in server code and client code, including values injected through CSR env injection.
import { env } from "@/env.mjs"
import { getEnvVar } from "@/lib/env-vars"
// OK
console.log(getEnvVar("TEST_VARIABLE"))
// Also works, but prefer getEnvVar()
console.log(env.TEST_VARIABLE)
// Do not read process.env directly
console.log(process.env.TEST_VARIABLE)
Strapi API Tokens
Strapi API tokens authenticate UI requests to the Strapi Content API. See the official Strapi API Tokens docs.
Read-Only Token
Open Strapi admin → Settings → API Tokens, then open the seeded Read Only token and click Regenerate.
Set the generated value in:
STRAPI_REST_READONLY_API_KEY=<paste-token-here>
The regenerated token is shown once.
Custom Token
Write operations (POST, PUT, DELETE) need a Custom token:
STRAPI_REST_CUSTOM_API_KEY=<custom-token>
Create it in Strapi admin → Settings → API Tokens.
Permissions are scoped manually per content type. Strapi currently does not provide a way to share API token permissions between environments through config files or another project-level sync mechanism.
Reference
| Var | Scope | Required | Used by |
|---|---|---|---|
APP_PUBLIC_URL | server | Yes | Canonical URL and metadata base URL; Better Auth baseURL; URL formatting. |
STRAPI_URL | server | Yes | Strapi base URL; Strapi clients; proxy routes. Required at build if pre-rendering ISR pages. |
STRAPI_REST_READONLY_API_KEY | server | Yes | Read-only Strapi API token for public content reads; public-proxy GET/HEAD auth; server-side reads. |
STRAPI_REST_CUSTOM_API_KEY | server | When used | Public-proxy writes (POST, PUT, DELETE). |
STRAPI_PREVIEW_SECRET | server | When used | Strapi preview route and preview listener. |
BETTER_AUTH_SECRET | server | When auth is used | Encrypts Better Auth session cookie. |
BASIC_AUTH_ENABLED | server | No | Enables Basic Auth for the whole app when needed. |
BASIC_AUTH_USERNAMEBASIC_AUTH_PASSWORD | server | When basic auth is enabled | Credentials for the edge basic auth gate. |
IMGPROXY_URL | server | No | External image optimization service. |
RECAPTCHA_SECRET_KEY | server | When reCAPTCHA is used | reCAPTCHA v3 verification. |
SENTRY_AUTH_TOKENSENTRY_ORGSENTRY_PROJECT | server | When source maps are uploaded | Sentry source-map upload at build time. |
SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING | server | No | Suppresses Sentry global error handler file warnings. |
DEBUG_STATIC_PARAMS_GENERATION | server | No | Static params generation debug logging. |
DEBUG_STRAPI_CLIENT_API_CALLS | server | No | Verbose Strapi client logging. |
SHOW_NON_BLOCKING_ERRORS | server | No | Logs non-blocking runtime errors. |
NEXT_OUTPUT | server | No | Keep undefined for local development, set to standalone for Docker builds, or export for static builds. See Docker Build and Next Configuration. |
NEXT_PUBLIC_SENTRY_DSN | client | When Sentry is used | Sentry browser SDK. |
NEXT_PUBLIC_RECAPTCHA_SITE_KEY | client | When reCAPTCHA is used | reCAPTCHA widget. |
NEXT_PUBLIC_PREVENT_UNUSED_FUNCTIONS_ERROR_LOGS | client | No | Sentry noise filter. |
NODE_ENV | shared | No | Standard Node.js environment mode. In this project it is development or production. |
APP_ENV | shared | No | Project deployment environment label. Used when the app needs to distinguish deployments such as testing and production independently from NODE_ENV. |
Environment Helpers
Use the helpers from src/lib/general-helpers.ts when code needs to branch by environment:
| Helper | Reads | Returns true when |
|---|---|---|
isProduction() | APP_ENV | APP_ENV is production. |
isTesting() | APP_ENV | APP_ENV is testing. |
isDevelopment() | NODE_ENV | NODE_ENV is development. |
Use APP_ENV for deployment-specific behavior such as production-only SEO output or testing deployments. Use NODE_ENV for framework/runtime mode checks such as local development behavior.
CSR Env Injection
CSR env injection lets the client/browser read selected environment values without rebuilding the whole frontend when those values change. In Docker, this also lets a single image serve multiple environments.
For public values that only need to be read by the browser at runtime, CSR env injection can avoid adding NEXT_PUBLIC_* variables that are baked into the frontend bundle during build.
The root layout reads selected env vars on the server and injects them into:
window.CSR_CONFIG
getEnvVar() reads from window.CSR_CONFIG on the client. Configure the allowlist via CSR_ENVs in:
apps/ui/src/app/[locale]/layout.tsx
window.CSR_CONFIG is serialized into the HTML response and is visible to anyone who views source or opens DevTools.
Only include values that are safe to publish: public API base URLs, GA/Sentry public DSNs, feature flags, or environment labels.
Never include API keys, JWTs, database URLs, Strapi STRAPI_REST_*_API_KEY values, OAuth client secrets, or any token that grants server-side access.