Environment Variables

Complete reference for every environment variable in Nebutra-Sailor — requirement level, default value, and service context.

Validation

All variables are validated by @nebutra/config at startup (packages/platform/config/src/index.ts). Required variables that are absent or empty cause an immediate, descriptive startup error. Optional variables that are absent fall back to the documented default.

Variables prefixed with NEXT_PUBLIC_ are bundled into the client-side JavaScript and are visible to end users. Never put secrets in NEXT_PUBLIC_ variables.


Database

VariableRequiredDefaultDescription
DATABASE_URLYesFull PostgreSQL connection string. Format: postgresql://user:pass@host:5432/nebutra
DATABASE_URL=postgresql://postgres:password@localhost:5432/nebutra

For local development, pnpm infra:up starts a PostgreSQL 16 instance on port 5432 with user postgres and password password.


Authentication

VariableRequiredDefaultDescription
AUTH_PROVIDERNobetter-authServer-side auth provider. One of better-auth, nextauth, clerk, supabase, or dev.
NEXT_PUBLIC_AUTH_PROVIDERNobetter-authClient-visible auth provider. Keep this aligned with AUTH_PROVIDER.
BETTER_AUTH_SECRETIf AUTH_PROVIDER=better-authSelf-hosted Better Auth signing secret. Generate with openssl rand -base64 32. Must match on auth-center and every RP app.
BETTER_AUTH_URLIf AUTH_PROVIDER=better-authAuth-center origin that serves /api/auth/*. Production: https://auth.nebutra.com.
NEXT_PUBLIC_AUTH_URLIf split auth-centerPublic auth-center URL for client redirects / SDK baseURL. Usually same as BETTER_AUTH_URL.
AUTH_COOKIE_DOMAINProduction multi-subdomainShared cookie parent domain (e.g. .nebutra.com) so session works on app + auth.
SERVICE_SECRETYes (gateway / S2S)HS256 secret for internal x-service-token JWTs. Not an end-user session secret.
AUTH_SECRETIf AUTH_PROVIDER=nextauthAuth.js / NextAuth signing secret. Generate with openssl rand -base64 32.
NEXTAUTH_URLIf AUTH_PROVIDER=nextauthDashboard origin for Auth.js callbacks.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYIf AUTH_PROVIDER=clerkClerk publishable key. Starts with pk_live_ or pk_test_.
CLERK_SECRET_KEYIf AUTH_PROVIDER=clerkClerk secret key. Starts with sk_live_ or sk_test_.
CLERK_WEBHOOK_SECRETIf Clerk webhooks are enabledSigning secret for Clerk webhook payloads. Starts with whsec_.
AUTH_SSO_DISCOVERY_PROVIDERSIf Enterprise SSO discovery is enabledJSON array mapping email domains to SSO providers. Clerk entries use /sign-in/sso; Feishu entries use /api/auth/oauth/feishu; generic entries must define an internal loginUrl.
FEISHU_APP_IDIf Feishu/Lark SSO is enabledFeishu/Lark app id for Better Auth generic OAuth.
FEISHU_APP_SECRETIf Feishu/Lark SSO is enabledFeishu/Lark app secret. Never expose this as NEXT_PUBLIC_.
FEISHU_OAUTH_SCOPESNocontact:user.email contact:user.base:readonlySpace- or comma-separated OAuth scopes requested from Feishu/Lark.
FEISHU_ALLOWED_TENANT_KEYSNoOptional allowlist of Feishu tenant keys accepted by this deployment.
FEISHU_REDIRECT_URINoOptional override. Default Better Auth callback is /api/auth/oauth2/callback/feishu.
GOOGLE_CLIENT_IDIf Google OAuth is enabledGoogle OAuth web client id for Better Auth or NextAuth.
GOOGLE_CLIENT_SECRETIf Google OAuth is enabledGoogle OAuth web client secret for Better Auth or NextAuth.
GITHUB_CLIENT_IDIf GitHub OAuth is enabledGitHub OAuth app client id (Better Auth).
GITHUB_CLIENT_SECRETIf GitHub OAuth is enabledGitHub OAuth app client secret.
NEXT_PUBLIC_GOOGLE_CLIENT_IDIf Google One Tap is enabled for Better Auth or NextAuthPublic Google OAuth web client id used by the landing page One Tap prompt.
NEXT_PUBLIC_ENABLE_GOOGLE_ONE_TAPNotrueSet to false to disable the landing One Tap prompt.
NEXT_PUBLIC_TURNSTILE_SITE_KEYIf Turnstile captcha is shownCloudflare Turnstile site key on auth-center forms.
TURNSTILE_SECRET_KEY / TURNSTILE_SECRETIf Turnstile is enforcedServer secret for siteverify. When unset, verification is skipped.
NEXT_PUBLIC_AUTH_MAGIC_LINKNooffSet 1 / true to show magic-link alternate on auth-center sign-in.
NEXT_PUBLIC_AUTH_PASSKEYSNooffSet 1 / true to show passkey button + conditional UI.
PASSKEY_RP_IDNoauth hostnameWebAuthn RP ID override (defaults to auth-center host).
PASSKEY_ORIGINNoauth originWebAuthn origin override (defaults to auth-center origin).
NEBUTRA_LANDING_ORIGINIf NextAuth One Tap is enabledLanding origin allowed to POST to /api/auth/google-one-tap.
NEBUTRA_SESSION_HINT_DOMAINNoOptional shared cookie domain for the lightweight cross-subdomain session hint.
AUTH_PROVIDER=better-auth
NEXT_PUBLIC_AUTH_PROVIDER=better-auth
BETTER_AUTH_SECRET=local-dev-secret
BETTER_AUTH_URL=http://localhost:3101
NEXT_PUBLIC_AUTH_URL=http://localhost:3101
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx
NEXT_PUBLIC_GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
AUTH_PROVIDER=better-auth
NEXT_PUBLIC_AUTH_PROVIDER=better-auth
BETTER_AUTH_SECRET=...                 # same on apps/auth + apps/web
BETTER_AUTH_URL=https://auth.nebutra.com
NEXT_PUBLIC_AUTH_URL=https://auth.nebutra.com
AUTH_COOKIE_DOMAIN=.nebutra.com
DATABASE_URL=postgresql://...
SERVICE_SECRET=...
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxx
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAA...
TURNSTILE_SECRET_KEY=0x4AAAAA...
# Optional UX flags on auth-center:
# NEXT_PUBLIC_AUTH_MAGIC_LINK=1
# NEXT_PUBLIC_AUTH_PASSKEYS=1
# PASSKEY_RP_ID=auth.nebutra.com
# PASSKEY_ORIGIN=https://auth.nebutra.com
AUTH_PROVIDER=clerk
NEXT_PUBLIC_AUTH_PROVIDER=clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxx
CLERK_SECRET_KEY=sk_live_xxx
CLERK_WEBHOOK_SECRET=whsec_xxx
AUTH_PROVIDER=better-auth
NEXT_PUBLIC_AUTH_PROVIDER=better-auth
BETTER_AUTH_SECRET=...
BETTER_AUTH_URL=https://auth.nebutra.com
NEXT_PUBLIC_AUTH_URL=https://auth.nebutra.com
AUTH_COOKIE_DOMAIN=.nebutra.com
FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=xxx
FEISHU_OAUTH_SCOPES="contact:user.email contact:user.base:readonly"
AUTH_SSO_DISCOVERY_PROVIDERS='[{"domain":"example.cn","id":"example-feishu","name":"Example Feishu","type":"oidc","provider":"feishu"}]'

OAuth redirect URIs must target the auth-center origin, e.g. https://auth.nebutra.com/api/auth/callback/google and https://auth.nebutra.com/api/auth/callback/github. GET https://auth.nebutra.com/health returns the currently enabled oauth.callbackUrls.


Application URLs

VariableRequiredDefaultDescription
NEXT_PUBLIC_APP_URLYesBase URL of the authenticated dashboard. Example: https://app.nebutra.com
NEXT_PUBLIC_API_URLYesBase URL of the API gateway. Example: https://api.nebutra.com

Payments (Stripe)

VariableRequiredDefaultDescription
STRIPE_SECRET_KEYYesStripe secret key. Starts with sk_live_ or sk_test_.
STRIPE_WEBHOOK_SECRETYesSigning secret for Stripe webhook payloads. Starts with whsec_.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYYesStripe publishable key. Starts with pk_live_ or pk_test_.
STRIPE_PRO_PRICE_IDYesStripe Price ID for the Pro plan. Starts with price_.
STRIPE_ENTERPRISE_PRICE_IDYesStripe Price ID for the Enterprise plan. Starts with price_.

Use test-mode keys (sk_test_, pk_test_) in development and preview environments. Never use live-mode keys outside production.


Email (Resend)

VariableRequiredDefaultDescription
RESEND_API_KEYYesResend API key. Starts with re_.
EMAIL_FROMYesSender address shown on all transactional emails. Example: Nebutra <[email protected]>

Storage

VariableRequiredDefaultDescription
STORAGE_PROVIDERNolocalStorage backend. One of s3, r2, s3-compatible, local.
AWS_BUCKET_NAMENonebutra-uploadsS3 or R2 bucket name.
AWS_REGIONNous-east-1AWS region for S3.
AWS_ACCESS_KEY_IDNoAWS access key ID.
AWS_SECRET_ACCESS_KEYNoAWS secret access key.

When STORAGE_PROVIDER is local, uploaded files are written to ./uploads on the server filesystem. This is suitable for local development only — local storage is not persistent across deployments.


AI

VariableRequiredDefaultDescription
OPENAI_API_KEYNoOpenAI API key. Required when FEATURE_AI_CHAT=true or FEATURE_AI_EMBEDDINGS=true.
OPENROUTER_API_KEYNoOpenRouter API key. Alternative to direct OpenAI access.
AI_DEFAULT_MODELNogpt-5.4-miniDefault model identifier used for AI completions.

Redis

VariableRequiredDefaultDescription
REDIS_URLNoRedis connection string. Required for queues (BullMQ), caching, and rate limiting. Example: redis://localhost:6379.

pnpm infra:up starts Redis 7 on port 6379. pnpm infra:lite starts only PostgreSQL — Redis is not included.


Analytics & Observability (ClickHouse)

VariableRequiredDefaultDescription
CLICKHOUSE_URLNohttp://localhost:8123ClickHouse HTTP endpoint. Required for the @nebutra/metering pipeline.
CLICKHOUSE_DBNonebutraClickHouse database name.

Error Tracking (Sentry)

VariableRequiredDefaultDescription
SENTRY_DSNNoSentry DSN for server-side error reporting.
NEXT_PUBLIC_SENTRY_DSNNoSentry DSN for client-side error reporting.

Product Analytics (PostHog)

VariableRequiredDefaultDescription
POSTHOG_KEYNoServer-side PostHog project API key for product events. Starts with phc_.
POSTHOG_HOSTNohttps://us.i.posthog.comServer-side PostHog ingestion host.
NEXT_PUBLIC_POSTHOG_KEYNoPostHog project API key. Starts with phc_.
NEXT_PUBLIC_POSTHOG_HOSTNohttps://app.posthog.comPostHog ingestion host. Override to use a self-hosted or EU instance.

Background Jobs (Inngest)

VariableRequiredDefaultDescription
INNGEST_EVENT_KEYNoInngest event API key for sending events.
INNGEST_SIGNING_KEYNoInngest signing key for webhook verification.

Serverless Queue (QStash)

These variables are required only when QUEUE_PROVIDER=qstash. If REDIS_URL is set and QUEUE_PROVIDER is unset, the system auto-selects BullMQ instead.

VariableRequiredDefaultDescription
QSTASH_TOKENConditionalUpstash QStash REST token.
QSTASH_CURRENT_SIGNING_KEYConditionalCurrent webhook signing key for payload verification.
QSTASH_NEXT_SIGNING_KEYConditionalNext signing key, used during key rotation.
QSTASH_CALLBACK_BASE_URLConditionalPublic base URL where QStash delivers webhook callbacks. Example: https://api.nebutra.com.

Generating secrets

For any secret that requires a cryptographically random value:

openssl rand -base64 32

Use a separate secret value per environment (development, preview, production). Never reuse secrets across environments.

How is this guide?

Edit on GitHub

Last updated on

On this page