Skip to Content
Self-HostingEnvironment Variables

Environment Variables

All configuration is provided via environment variables. Set them in .env.local for local development, or as container environment variables for Docker/EC2 deployments.

Required

VariableDescription
NEXT_PUBLIC_SUPABASE_URLYour Supabase project URL (e.g., https://xyz.supabase.co). Used by the browser and as the fallback for server-side connections when SUPABASE_INTERNAL_URL is not set. Also used to derive the cookie name for session management.
NEXT_PUBLIC_SUPABASE_ANON_KEYSupabase anon key (public, used in browser)
SUPABASE_SERVICE_ROLE_KEYSupabase service role key (secret, server-only)

Optional — Networking

VariableDescription
SUPABASE_INTERNAL_URLServer-only override for the Supabase URL. When set, all server-to-Supabase connections — including cookie-based SSR requests — use this URL instead of NEXT_PUBLIC_SUPABASE_URL. Cookie-based clients automatically have their cookie name overridden to match the one derived from NEXT_PUBLIC_SUPABASE_URL, so browser sessions continue to work correctly. The browser itself always uses NEXT_PUBLIC_SUPABASE_URL. Useful in Docker/Kubernetes environments where the server can reach Supabase over an internal network (e.g. http://supabase-kong:8000), avoiding egress through a public load balancer entirely.

Optional — Application

VariableDefaultDescription
DEFAULT_ADMIN_EMAILadmin@example.comAdmin account email used by the setup script and on first startup.
DEFAULT_ADMIN_PASSWORD(none)Admin account password applied on startup only if the password has never been changed. Must be set to a strong secret in production.
MAX_BATCH_MUTATION100Maximum number of items that can be created, updated, or deleted in a single batch request.
FILES_MAX_UPLOAD_SIZE104857600Fallback only. Used when the target bucket has no File size limit set in Supabase Studio. Set the limit on the bucket instead — see Upload limits.
FILES_MIME_TYPE_ALLOW_LIST(all allowed)Fallback only. Used when the target bucket has no Allowed MIME types set in Supabase Studio. Comma-separated, e.g. image/png,image/jpeg; image/* wildcards are accepted.
FILES_STORAGE_BUCKET_ALLOW_LISTfilesComma-separated storage buckets that signed uploads may target. Requests naming any other bucket are rejected with 400.
STORAGE_IMPORT_ALLOWED_NETWORKS(none)Comma-separated addresses or CIDR prefixes that POST /api/files/import may fetch from although they are not publicly routable, e.g. 10.20.0.0/16,192.168.5.10. With no value set, the import refuses loopback, private, link-local, carrier-grade NAT, multicast, and reserved addresses with 400 IMPORT_URL_REFUSED. An entry that is not an address or a prefix is logged and allows nothing. The Go engine reads the same variable. Not listed in docker-compose.yml — add it to the environment list if you need it.
FILES_UPLOAD_TOKEN_SECRET(falls back to SUPABASE_SERVICE_ROLE_KEY)HMAC key for signing upload tokens. Set this to a dedicated secret if you rotate the service role key independently — rotating the signing key invalidates every upload token that is still in flight.
JSONB_SEARCH_MATCH_LIMIT150Maximum number of primary keys returned by the daas_search_item_ids RPC when ?search= targets a collection with json/jsonb columns. Keys are passed via the request URL (?id=in.(…)); very large lists can exceed proxy URL length limits (HTTP 414). Increase only if your reverse proxy allows longer URLs and your collections have json/jsonb columns with high result cardinality.
ACTIVITY_EVENT_RATE_LIMIT60Requests that one caller can send to POST /api/activity/event in each window. A caller over the limit gets 429 with the code REQUESTS_EXCEEDED and a Retry-After header. Each worker process counts separately, so the true ceiling is this value multiplied by the worker count.
ACTIVITY_EVENT_RATE_WINDOW_MS60000Length of the rate-limit window for POST /api/activity/event, in milliseconds.

Optional — Authentication

VariableDefaultDescription
MFA_FORCE_ENABLEDfalseOrg-wide mandatory two-factor authentication. When true, a user with no verified TOTP factor is blocked from every route except the MFA enroll and verify endpoints, and the login page walks them through enrollment right after password sign-in. Removing an existing factor is rejected at the API, not only hidden in the UI. Static tokens (daas_users.token) are always exempt — use one for service and automation accounts under this policy. Off by default, which leaves self-service (opt-in) MFA unaffected. Read at runtime, so no rebuild is needed; restart the server or container after a change. docker-compose.yml does not list this variable — add it to your own environment.
EXTENSION_ALLOWED_DOMAINS(none)Comma-separated hosts that stored code (extensions, cron jobs, custom services) may reach through safeFetch. Required for any outbound request. With no value set, only localhost is reachable and every other host is denied. Example: api.example.com,hooks.example.com. See Extensions.
BUILDPAD_SSO_SECRET(unset)HMAC key that signs the short-lived SSO handoff token from the BuildPad platform. POST /api/auth/sso answers 500 while it is unset, which disables SSO auto-login. The platform side must hold the same secret. There is no fallback to another secret. The endpoint accepts 10 requests per minute for each IP address, refuses a token that is older than 5 minutes, and refuses a second exchange of the same token.

Optional — Performance & Scaling

VariableDefaultDescription
WEB_CONCURRENCY(one worker per core)Number of app worker processes (Docker only — the image starts cluster.js). Each worker is a full Next.js server (~400 MB resident under load). Set explicitly whenever the container memory limit is below cores × 400 MB, or the container OOM-loops on startup (e.g., a 1 GB limit → WEB_CONCURRENCY=2). See Docker.
SUPABASE_JWT_SECRET(unset)Project JWT secret (Supabase Settings → API → JWT Settings). When set, HS256 access tokens are verified in-process instead of a GoTrue round trip per request. Only applies to users with an active daas_users row; everyone else falls back to GoTrue. Trade-off: a session revoked via logout stays valid until token expiry.
PERMISSION_CACHE_TTL_MS30000TTL for cross-request permission caches (policy IDs, permission rows, admin/status flags, static tokens). Permission changes made through the API invalidate immediately; the TTL only bounds staleness for direct-SQL writes. 0 disables cross-request reuse.

Optional — Shared Cache (Valkey)

Off unless VALKEY_URL or VALKEY_SENTINEL_NODES is set. When one is, permission, auth-status and schema-metadata lookups are shared between worker processes and containers, so a cold process does not reload them from the database. Invalidation stays immediate either way, and while Valkey is unreachable the app keeps serving from its in-memory caches and retries the connection. See Docker.

VariableDefaultDescription
VALKEY_URL(unset — cache off)redis:// or rediss:// (TLS) URL. Comma-separate seed nodes in cluster mode.
VALKEY_MODEstandalone, or sentinel when VALKEY_SENTINEL_NODES is setstandalone, cluster, or sentinel.
VALKEY_SENTINEL_NODES(unset)host:port list. The app asks the sentinels for the current master, and re-asks them after a failover drops the connection.
VALKEY_SENTINEL_MASTERmymasterMaster name the sentinels monitor.
VALKEY_SENTINEL_PASSWORD(unset)AUTH for the sentinels themselves, when they require one.
VALKEY_USERNAME / VALKEY_PASSWORD(unset)Server credentials. Override any credentials carried in VALKEY_URL.
VALKEY_TLSfalseForce TLS. A rediss:// URL implies it.
VALKEY_HOST_OVERRIDE(unset)Connect to this host instead of the one a sentinel reports. For networks where the sentinel’s view of the master is not reachable under that name.
VALKEY_KEY_PREFIXdaasKey namespace. Give every environment that shares one Valkey its own prefix.
VALKEY_REQUEST_TIMEOUT_MS250Per-command timeout. After a failed command the shared cache pauses briefly, so one request pays the timeout instead of every lookup of every request.
VALKEY_CONNECT_TIMEOUT_MS2000Connection and sentinel-discovery timeout. Connecting never blocks a request — lookups take their in-process path until it succeeds.
VALKEY_RETRY_MS30000Wait before reconnecting after a failure.
VALKEY_SESSION_CACHE_TTL_MS0 (off)Remember access tokens GoTrue has accepted for this long, so repeat requests skip the GoTrue round trip. Requires Valkey. Trade-off: a session revoked outside this app — through the GoTrue admin API, or by a password change in another client — keeps working here for up to this TTL. Logout through the app, user suspension and the MFA checks still take effect immediately. Keep it short, for example 60000.

Optional — Runtime Env Whitelisting

These variables control which environment values are exposed to runtime automation code (extensions, cron jobs, custom services).

VariableDefaultDescription
RUNTIME_ENV_WHITELIST_GLOBAL(empty)Comma-separated keys exposed to all runtime scopes
RUNTIME_ENV_WHITELIST_EXTENSION(empty)Comma-separated keys exposed only to runtime extensions
RUNTIME_ENV_WHITELIST_CRON(empty)Comma-separated keys exposed only to cron jobs
RUNTIME_ENV_WHITELIST_CUSTOM_SERVICE(empty)Comma-separated keys exposed only to custom services

Prefix-based exposure is also supported:

  • RUNTIME_PUBLIC_* (all scopes)
  • EXTENSION_PUBLIC_* (extensions only)
  • CRON_PUBLIC_* (cron only)
  • CUSTOM_SERVICE_PUBLIC_* (custom services only)

For full examples and code usage, see Runtime Env Whitelisting.

Optional — CORS

These variables are the static fallback only. Once migrations have run, CORS is governed by the values in daas_settings (editable at Settings → CORS) and cached for 60 seconds; the env vars apply only when the settings row cannot be read. See CORS.

VariableDefaultDescription
CORS_ENABLEDtrueWhether CORS headers are set
CORS_ORIGINS(empty — blocks all)Comma-separated allowed origins
CORS_METHODSGET,HEAD,POST,PUT,PATCH,DELETE,OPTIONSAllowed HTTP methods
CORS_ALLOWED_HEADERSContent-Type, Authorization, Origin, X-Requested-With, Accept, X-Resource-UriAllowed request headers
CORS_EXPOSE_HEADERS(empty)Headers exposed to the client
CORS_ALLOW_CREDENTIALSfalseSet Access-Control-Allow-Credentials
CORS_MAX_AGE600Preflight cache duration in seconds

See CORS for examples.

Optional — SMTP (Email)

SMTP can be configured in Settings → SMTP (stored in daas_settings) or via environment variables. Settings values take precedence — on a migrated deployment the boolean and port fields always come from the database, so SMTP_ENABLED=true alone will not enable mail; enable it in Settings → SMTP. Env vars act as a fallback for fields left empty in Settings and when the settings row cannot be read.

VariableDefaultDescription
SMTP_ENABLEDfalseEnable SMTP email sending
SMTP_HOST(empty)SMTP server hostname
SMTP_PORT587SMTP port
SMTP_USER(empty)SMTP username
SMTP_PASSWORD(empty)SMTP password
SMTP_FROM_EMAIL(empty)Sender email address (SMTP_FROM accepted as alias)
SMTP_FROM_NAME(empty)Sender display name
SMTP_SECUREfalsetrue for SSL/TLS, false for STARTTLS
SMTP_IGNORE_TLSfalseSkip TLS entirely (not recommended in production)

Security Notes

  • Never commit .env.local to version control
  • SUPABASE_SERVICE_ROLE_KEY grants full database access — keep it secret
  • NEXT_PUBLIC_* variables are embedded in the browser bundle — never put secrets in them
  • SUPABASE_INTERNAL_URL has no NEXT_PUBLIC_ prefix and is never sent to the browser
  • MFA_FORCE_ENABLED does not apply to static tokens (daas_users.token). A static token authenticates without a session, so it carries no assurance level. Treat a static token as an exemption from the MFA policy, and issue one only to service accounts