Environment Variables
Runtime configuration variables used by the application.
This page lists the runtime variables consumed by the application. Use Deployment Environment Setup for the operational runbook that explains where to retrieve each value, how to scope hosted provider environments, and how to validate a deployment environment before promotion.
Core
NODE_ENV=development
NEXT_PUBLIC_BASE_URL=http://localhost:3080
DATABASE_URL=postgresql://user:password@localhost:5432/yayaw
DATABASE_POOL_MAX=
DATABASE_IDLE_TIMEOUT_SECONDS=
DATABASE_MAX_LIFETIME_SECONDS=
DATABASE_CONNECT_TIMEOUT_SECONDS=
DATABASE_STATEMENT_TIMEOUT_SECONDS=
DATABASE_PREPARE_STATEMENTS=
CMS_DASHBOARD_FULL_METRICS=false
BETTER_AUTH_SECRET=
BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3080- The
DATABASE_*pool knobs are optional. Runtime DB pools default to10connections in development and3in production/serverless. Production also defaults to10seconds connect timeout,20seconds idle timeout,300seconds max lifetime,20seconds statement timeout, and disabled prepared statements for pooler compatibility. RaiseDATABASE_POOL_MAXor the lifecycle values only when the deployment target can support the extra per-runtime connections. BETTER_AUTH_SECRETmust be a stable, high-entropy server-only secret in production and self-hosted runtimes.DATABASE_STATEMENT_TIMEOUT_SECONDSapplies a per-statement timeout when the database client opens a connection. Leave it empty unless the target Postgres provider or self-hosted database needs an explicit guardrail.CMS_DASHBOARD_FULL_METRICS=trueenables the full CMS publication and activity query set on/dashboard/content. Leave it false by default so the CMS dashboard uses fast indexed counters and never blocks the dashboard shell on expensive aggregate reads.
Build-Time Knobs
NEXT_BUILD_WORKERS=1
NEXT_STATIC_PAGE_GENERATION_TIMEOUT=180NEXT_BUILD_WORKERScontrols the number of Next.js workers used duringnext build. Self-hosted Docker builds default to1for stability on smaller hosts.NEXT_STATIC_PAGE_GENERATION_TIMEOUTraises Next.js' static generation timeout for slower builders. Increase it only when legitimate static pages time out.
Production Host
Yayaw production uses https://yayaw.app as the canonical public origin.
Preview deployments use https://preview.yayaw.app for the branch-backed
preview domain.
NEXT_PUBLIC_BASE_URL=https://yayaw.app
BETTER_AUTH_TRUSTED_ORIGINS=https://*.yayaw.app,https://*.vercel.appNEXT_PUBLIC_BASE_URLis the application source of truth for canonical URLs, Better Auth base URL, OAuth metadata, sitemap/robots links, and generated absolute asset URLs.NEXT_PUBLIC_SITE_URLis not read by the application.BETTER_AUTH_URLis not required by the current runtime because Better Auth receivesbaseURLfromNEXT_PUBLIC_BASE_URL. If a legacy deployment still defines it, keep it aligned withhttps://yayaw.app.preview.yayaw.appis the branch-backed preview domain and tracks the durablepreviewGit branch.- Keep
www.yayaw.appas a Vercel project-domain redirect toyayaw.app; the app also normalizeswwwpage requests before i18n routing. - Keep retired
.euhosts only as Vercel308redirects to their.appreplacements, such asyayaw.euandwww.yayaw.eutoyayaw.app. Do not add retired hosts toBETTER_AUTH_TRUSTED_ORIGINS. - Organization public domains are separate from the canonical app host. They
are verified through the configured public-domain provider and then mapped
through
organization_public_domains; they do not belong inBETTER_AUTH_TRUSTED_ORIGINSbecause dashboard/auth are not served from those hosts.
Billing and Stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_ONE_TIME_WEBHOOK_SECRET=
BILLING_GRACE_PERIOD_DAYS=7
BILLING_PRO_SEAT_LIMIT=10
BILLING_BUSINESS_SEAT_LIMIT=100
BILLING_CODE_ACCESS_REPOSITORY_URL=
BILLING_CODE_ACCESS_DOWNLOAD_URL=
BILLING_CODE_ACCESS_DOCUMENTATION_URL=
BILLING_CODE_ACCESS_SUPPORT_URL=
BILLING_CODE_ACCESS_GITHUB_REPOSITORY=
BILLING_CODE_ACCESS_GITHUB_APP_ID=
BILLING_CODE_ACCESS_GITHUB_APP_INSTALLATION_ID=
BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEY=
BILLING_CODE_ACCESS_GITHUB_TOKEN=STRIPE_WEBHOOK_SECRETis used by the Better Auth Stripe plugin webhook endpoint.STRIPE_ONE_TIME_WEBHOOK_SECRETis used by the custom one-time webhook endpoint.- Billing product prices are managed from admin or MCP and synced to Stripe; resulting Stripe price IDs are stored internally in
billing_products. BILLING_CODE_ACCESS_*URLs are optional non-secret deliverable links shown on/dashboard/organization/code-accessafter an eligible purchase or active subscription. Leave a value empty when that deliverable requires manual provisioning.- GitHub repository access for paid code access is configured from
/dashboard/admin/billing-settings. Non-secret values can also be supplied as environment fallbacks withBILLING_CODE_ACCESS_GITHUB_REPOSITORY,BILLING_CODE_ACCESS_GITHUB_APP_ID, andBILLING_CODE_ACCESS_GITHUB_APP_INSTALLATION_ID. - Keep GitHub secrets in environment variables only:
BILLING_CODE_ACCESS_GITHUB_APP_PRIVATE_KEYfor production GitHub App provisioning, orBILLING_CODE_ACCESS_GITHUB_TOKENas an optional local/staging fallback. - See Deployment Environment Setup for the GitHub App creation, installation ID, private key, and token fallback steps.
Canonical Host and Auth Sessions
- Set
NEXT_PUBLIC_BASE_URLto the canonical production host. - Set preview
NEXT_PUBLIC_BASE_URLto the branch-backed preview host when a stable preview domain is configured. - Do not mix
wwwand non-wwwhosts for authenticated sessions. - Keep
BETTER_AUTH_TRUSTED_ORIGINSfor required preview/local hosts. The canonical host and itswwwvariant are already derived fromNEXT_PUBLIC_BASE_URL. - For locale-prefixed routes, prefer
@/i18n/navigation(Link,useRouter,usePathname) instead ofnext/linkandnext/navigationin app navigation components.
OAuth (Optional)
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=Email (Optional but required for invitation/reset/magic-link features)
RESEND_API_KEY=
EMAIL_SENDER=team@yayaw.app
EMAIL_SUPPORT=support@yayaw.app
EMAIL_USERNAME=Yayaw TeamEMAIL_SENDER is the verified Resend sender address used in From headers.
EMAIL_SUPPORT is shown in templates and support copy. EMAIL_USERNAME is the
display name paired with the sender address. These variables are bootstrap
defaults and fallbacks; runtime values saved in Admin > Site Settings > Email
take priority after setup.
Storage (Optional for media features)
Media storage is provider-backed. STORAGE_PROVIDER=supabase keeps the hosted
Supabase path. STORAGE_PROVIDER=s3 uses an S3-compatible API such as MinIO,
AWS S3, or R2. When STORAGE_PROVIDER is empty, the runtime selects Supabase
when Supabase storage env is present and S3 when the S3 env set is complete.
STORAGE_PROVIDER=
STORAGE_MEDIA_BUCKET=media
STORAGE_PUBLIC_BASE_URL=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
S3_ENDPOINT=
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_FORCE_PATH_STYLE=trueSTORAGE_MEDIA_BUCKETdefaults tomedia.STORAGE_PUBLIC_BASE_URLis required for S3-compatible storage and must be a public URL that can serve/<bucket>/<object-key>.- If
STORAGE_PUBLIC_BASE_URLuses the app or CDN origin, route each public bucket prefix to the object store before the app fallback. The built-in public prefixes are/media/*and/organization-logos/*. - Supabase storage requires
NEXT_PUBLIC_SUPABASE_URLandSUPABASE_SERVICE_ROLE_KEYfor server-side writes. The anon key remains available for client integrations but is not the server write credential. - S3-compatible storage requires endpoint, region, access key, secret key, and
public base URL. Keep
S3_FORCE_PATH_STYLE=truefor MinIO and most local S3 compatible endpoints. bun run seeduploads default global site-variable assets intoSTORAGE_MEDIA_BUCKETwhen storage is configured. Without storage credentials, the seed keeps local public-asset fallbacks so local setup can still complete.- Existing media rows store absolute public URLs, so changing providers later requires either keeping old URLs reachable or running a deliberate media URL migration.
OpenAI (Optional for AI builder features)
OPENAI_API_KEY=
OPENAI_COMPONENTS_AI_FALLBACK=true
OPENAI_IMAGE_GENERATION_ENABLED=true
OPENAI_IMAGE_MODEL=gpt-image-1.5
PAGE_AI_QUEUE_DRIVER=direct
PAGE_AI_DEEP_REFINEMENT=false
PAGE_AI_WORKER_POLL_MS=1500
PAGE_AI_WORKER_ID=OPENAI_COMPONENTS_AI_FALLBACKcontrols text/object AI fallbacks for component and page builder flows.OPENAI_IMAGE_GENERATION_ENABLEDcontrols page-builder image generation.- Runtime site settings can also disable these AI features through
ai-components-enabledandmedia-image-generation-enabledwithout changing environment variables. - Generated page-builder images use the configured OpenAI image model, default
to
gpt-image-1.5, and are stored aswebpmedia assets through the organization media library. PAGE_AI_QUEUE_DRIVERcontrols the durable Page AI wake-up transport:directfor local development,vercel-queuefor Vercel, anddb-workerfor a long-lived worker process. In production, the default isvercel-queueonly when Vercel runtime variables are present; otherwise it isdb-worker.PAGE_AI_DEEP_REFINEMENTis an internal environment-only quality toggle and is not exposed as an admin site setting.PAGE_AI_WORKER_POLL_MSandPAGE_AI_WORKER_IDare only used bybun run worker:page-aiwhenPAGE_AI_QUEUE_DRIVER=db-worker.
PostHog (Optional for analytics and flags)
NEXT_PUBLIC_ANALYTICS_PROVIDER=posthog
NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE=hybrid
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
NEXT_PUBLIC_POSTHOG_ENABLE_LOCAL=false
POSTHOG_PERSONAL_API_KEY=
POSTHOG_PROJECT_ID=
POSTHOG_API_HOST=https://eu.posthog.com
POSTHOG_ORG_ID_PROPERTY=organization_id
POSTHOG_FLAG_LOOKUP_TIMEOUT_MS=NEXT_PUBLIC_POSTHOG_* values are used for capture, feature flags, and client
identification. POSTHOG_PERSONAL_API_KEY, POSTHOG_PROJECT_ID, and
POSTHOG_API_HOST are private server-only values used by /dashboard analytics
to query PostHog through the private Query API. POSTHOG_ORG_ID_PROPERTY
defaults to organization_id and must match the event property registered for
organization-scoped dashboard metrics. POSTHOG_FLAG_LOOKUP_TIMEOUT_MS
optionally shortens or lengthens the server-side PostHog feature-flag lookup
timeout; it defaults to 1500.
NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE controls where events are captured:
hybrid keeps browser analytics for product behavior and sends server-side
billing/auth events, server disables browser analytics scripts and tracks CMS
page views plus conversions from the server, and client preserves legacy
browser-only capture. Server-side billing events include conversion properties
such as revenue, value, currency, plan, product_key, and Stripe IDs.
Umami (Optional Analytics Provider)
NEXT_PUBLIC_ANALYTICS_PROVIDER=umami
NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE=hybrid
NEXT_PUBLIC_UMAMI_HOST_URL=
NEXT_PUBLIC_UMAMI_SCRIPT_URL=
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_DOMAINS=
NEXT_PUBLIC_UMAMI_AUTO_TRACK=true
UMAMI_API_URL=
UMAMI_WEBSITE_ID=
UMAMI_API_TOKEN=
UMAMI_API_KEY=
UMAMI_USERNAME=
UMAMI_PASSWORD=
UMAMI_CMS_EVENT_PAGE_SIZE=1000NEXT_PUBLIC_UMAMI_* values configure the browser tracking script and are
baked into the client bundle at build time. UMAMI_API_URL, UMAMI_WEBSITE_ID,
and either UMAMI_API_TOKEN (or the legacy alias UMAMI_API_KEY) or
UMAMI_USERNAME plus UMAMI_PASSWORD are server-only values used by dashboard
analytics data providers.
For a no-browser-analytics setup, set NEXT_PUBLIC_ANALYTICS_CAPTURE_MODE=server.
The app will not render the Umami script, and server events are sent directly to
Umami /api/send with a server User-Agent. This improves privacy and avoids
client cookies, but session, unique visitor, device, and referrer quality is less
precise than browser capture.
Control Plane MCP (Optional)
YAYAW_MCP_API_KEY=
YAYAW_MCP_LOCAL_USER_ID=YAYAW_MCP_API_KEYis used by the local stdio MCP launcher when you want local development to verify a real Better Auth API key.YAYAW_MCP_LOCAL_USER_IDis only used by the local stdio launcher when no API key is provided.- Production MCP clients connect to
/api/mcpwithAuthorization: Bearer <Yayaw API key or OAuth access token>and should store secrets outside the repository. - OAuth MCP metadata is derived from
NEXT_PUBLIC_BASE_URL; production ChatGPT/App clients need that value to be the public HTTPS origin so issuer, resource, JWKS, and redirect metadata are stable.
Maintenance Mode (Optional)
MAINTENANCE_MODE=false
MAINTENANCE_MODE_END_DATE=Deployment Runtime
DEPLOYMENT_PROVIDER=
DEPLOYMENT_URL=
DEPLOYMENT_ENV=
DEPLOYMENT_GIT_COMMIT_SHA=
DEPLOYMENT_GIT_COMMIT_REF=
PUBLIC_DOMAIN_PROVIDER=
APP_MANAGED_HOSTS=
RESERVED_PUBLIC_DOMAIN_SUFFIXES=
PUBLIC_DOMAIN_CNAME_TARGET=
PUBLIC_DOMAIN_IPV4_TARGETS=
PUBLIC_DOMAIN_TXT_PREFIX=_yayaw
VERCEL_URL=
VERCEL_TOKEN=
VERCEL_PROJECT_ID=
VERCEL_TEAM_ID=DEPLOYMENT_PROVIDERcan bevercel,static, orlocal. Leave it empty to auto-detect Vercel fromVERCEL/VERCEL_URL, static deployments fromDEPLOYMENT_URL, and local otherwise.DEPLOYMENT_URL,DEPLOYMENT_ENV,DEPLOYMENT_GIT_COMMIT_SHA, andDEPLOYMENT_GIT_COMMIT_REFprovide dashboard/control-plane deployment metadata for Docker or other non-Vercel runtimes.PUBLIC_DOMAIN_PROVIDERcan bevercelormanual-dns. When unset, Vercel is selected only ifVERCEL_PROJECT_IDandVERCEL_TOKENare configured; otherwise manual DNS verification is used.APP_MANAGED_HOSTSadds comma-separated app-owned hosts that organization public domains may not claim.RESERVED_PUBLIC_DOMAIN_SUFFIXESadds suffixes, such as.preview.example, that custom public domains may not claim..vercel.appis always reserved.PUBLIC_DOMAIN_CNAME_TARGET,PUBLIC_DOMAIN_IPV4_TARGETS, andPUBLIC_DOMAIN_TXT_PREFIXdrive the manual DNS provider's hints and ownership TXT challenge.VERCEL_URLis supplied by Vercel and lets the app recognize deployment hosts as managed app hosts.VERCEL_TOKEN,VERCEL_PROJECT_ID, and optionalVERCEL_TEAM_IDare server-only values used to add, inspect, and verify organization public domains through the Vercel project-domain API.
Source of Truth
Keep .env.example in sync with real usage in source code when adding or removing variables.
Document retrieval steps in
Deployment Environment Setup whenever a
deployment operator needs to collect the value from an external provider.
Local Tooling Troubleshooting
Some local machines may hit an esbuild service hang when running Drizzle or docs generators.
If a command appears blocked:
- Use the safe scripts with timeouts:
bun run docs:generate
bun run db:generate
bun run db:push- If it still fails, reinstall dependencies:
rm -rf node_modules
bun install- Retry the command and verify with:
bun run check
bun run build