Browse documentation

Motherbase reads configuration from .env. The supported setup path is ./setup.sh, which writes the project and Console environment files and generates strong secrets.

For manual development, copy apps/server/.env.example to apps/server/.env and fill the required values. The Console has its own file, deploy/console/.env.example.

For the deployment-side view of the same variables — which file writes them, which stack consumes them — see self-hosting environment variables.

Generate secrets

# JWT secret
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
 
# Config encryption key, base64-encoded 32 bytes
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Required to boot

VariableDescription
DATABASE_URLPostgreSQL connection string
JWT_SECRETSecret for signing JWT access tokens

CONFIG_ENCRYPTION_KEY is not required to boot, but it is required to save Studio-managed secrets such as edge secrets and encrypted provider settings. setup.sh generates it.


Server

VariableDefaultDescription
PORT5001HTTP API server port
NODE_ENVdevelopmentdevelopment, test, or production
BASE_HOSTlocalhost:5001Public host used to generate file/API URLs
CORS_ORIGINSemptyComma-separated allowed origins. Empty is dev-only; production must use an explicit allowlist.
APP_NAMEMotherbaseProduct name in transactional email subjects/bodies and the default TOTP issuer shown in an authenticator app
BIND_HOSTprod 0.0.0.0, otherwise 127.0.0.1Interface to bind. Outside production, error responses carry stack traces, so the default keeps them off the network. Set it explicitly only for a VM or a remote dev box — doing so logs a warning
TRUST_PROXYempty (false)Express trust proxy. Required behind Traefik or any reverse proxy, otherwise every client shares the proxy IP in one rate-limit bucket. Accepts a hop count (1), loopback, or a comma-separated IP/CIDR list. Leave unset with no proxy in front — trusting X-Forwarded-For then lets clients forge their IP.

Database

VariableDefaultDescription
DATABASE_URLrequiredFull PostgreSQL connection string
POSTGRES_PORT55432PostgreSQL port used by Docker Compose
POSTGRES_USERpostgresDatabase user
POSTGRES_PASSWORDgenerated by setupDatabase password
POSTGRES_DBappDatabase name

Auth

VariableDefaultDescription
JWT_SECRETrequiredJWT signing secret
ACCESS_TOKEN_EXP_MIN15Access token TTL in minutes
REFRESH_TOKEN_EXP_DAYS30Refresh token TTL in days

Passwords are hashed with argon2. Imported Supabase bcrypt hashes are accepted and migrated to argon2 after the user's first successful password login.

Google, Apple, OTP, SMTP, and password reset behavior are configured per project in Project Studio -> Settings with environment fallbacks for SMTP.


Rate limits

VariableDefaultDescription
RATE_LIMIT_WINDOW_MIN15Rate limit window in minutes
RATE_LIMIT_GLOBAL_MAX1000Global request limit per window
RATE_LIMIT_AUTH_MAX20Auth route limit per window
RATE_LIMIT_REST_READ_MAX300REST read limit per window
RATE_LIMIT_REST_WRITE_MAX120REST write limit per window
RATE_LIMIT_EDGE_MAX60Edge invocation limit per window

REST API

VariableDefaultDescription
REST_MAX_ROWS1000Seed for the api.rest → maxRows setting. The query parser enforces a hard 1000 ceiling regardless
REST_RPC_SECURITY_DEFINERemptyComma-separated names of SECURITY DEFINER functions deliberately exposed over /rest/v1/rpc/. Empty by default — a definer function runs as its owner and bypasses the caller's RLS, so exposing one is always an explicit act
Note

This seeds the api.rest → rpcSecurityDefiner setting while no row has been saved for that group. Once it is saved, the stored value wins and this variable is no longer read — which is what makes clearing the allowlist from Studio actually clear it. Use Reset to .env on the group to hand control back to the environment.


Email / SMTP

Required for email OTP, password reset, and email verification.

VariableDefaultDescription
EMAIL_SMTP_HOSTemptySMTP server hostname
EMAIL_SMTP_USERNAMEemptySMTP login username
EMAIL_SMTP_PASSWORDemptySMTP login password
EMAIL_CODE_LENGTH6Number of digits in OTP codes
EMAIL_CODE_EXP_TIME2OTP expiry in hours

Storage

VariableDefaultDescription
STORAGE_DRIVERlocallocal or s3
STATIC_PATHuploadsLocal storage root directory
USER_PATHusersLegacy user-scoped subdirectory
MAX_UPLOAD_MB25Max multipart upload size in MB
MAX_BACKUP_UPLOAD_MB2048Separate ceiling for uploading a backup file into BACKUP_DIR. A database dump is routinely far larger than a tenant upload, and raising MAX_UPLOAD_MB to fit one would widen the surface on every public upload route

S3 / MinIO / R2

VariableDefaultDescription
S3_ENDPOINTemptyS3 endpoint URL, for example MinIO or R2
S3_REGIONus-east-1S3 region
S3_ACCESS_KEY_IDemptyS3 access key ID
S3_SECRET_ACCESS_KEYemptyS3 secret access key
S3_BUCKETuploadsPrivate physical bucket
S3_PUBLIC_BUCKETpublicPublic physical bucket
S3_PUBLIC_URLemptyPublic base URL for served files
MINIO_PORT59000Host port for the MinIO S3 API in Docker Compose
MINIO_CONSOLE_PORT59001Host port for the MinIO web console in Docker Compose

Logical buckets are configured in Project Studio and map onto the physical public/private buckets.


Backups and imports

VariableDefaultDescription
BACKUP_DIRbackupsLocal directory for database dumps and temporary Supabase import files
BACKUP_ENABLEDfalseEnable scheduled automatic database backups
BACKUP_CRON0 3 * * *Schedule for automatic backups
BACKUP_RETENTION7Number of local backups to retain
BACKUP_OFFSITEfalseUpload scheduled backups to S3-compatible storage
BACKUP_BUCKETbackupsDedicated offsite backup bucket
BACKUP_S3_ENDPOINTemptyOptional offsite S3 endpoint override
BACKUP_S3_REGIONemptyOptional offsite S3 region override
BACKUP_S3_ACCESS_KEY_IDemptyOptional offsite S3 access key override
BACKUP_S3_SECRET_ACCESS_KEYemptyOptional offsite S3 secret key override
PG_BIN_DIRemptyDirectory containing pg_dump / psql when they are not on PATH (common on native Windows/macOS dev machines)

The Supabase importer writes a temporary dump file under BACKUP_DIR and removes it after the import.

Offsite upload happens only when STORAGE_DRIVER=s3 and offsite is enabled. See backups and restore.


Settings seeds

These are fallbacks, not live configuration. Each seeds a field in a settings group while no row has been saved for that group; once the group is saved in Studio the stored value wins and the variable is no longer read. Use Reset to .env on a group to hand control back. See settings system.

VariableDefaultSeeds
AUTH_ALLOW_SIGNUPtrueauth.signup → allowNewUsers (any value but false is true)
AUTH_REQUIRE_EMAIL_VERIFICATIONfalseauth.signup → requireEmailVerification
AUTH_AUTO_SIGNINtrueauth.signup → autoSignInAfterVerify
AUTH_MIN_PASSWORD_LENGTH8auth.signup → minPasswordLength
AUTH_MAX_PASSWORD_LENGTH128auth.signup → maxPasswordLength
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETemptyauth.social → Google provider (present ⇒ enabled)
APPLE_CLIENT_ID / APPLE_CLIENT_SECRET / APPLE_BUNDLE_IDemptyauth.social → Apple provider
TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_FROM_NUMBERemptyphone.sms
GITHUB_OAUTH_CLIENT_ID / GITHUB_OAUTH_CLIENT_SECRET / GITHUB_OAUTH_CALLBACK_URLemptygit.github
STORAGE_RLS_POLICIESfalsestorage.policies → enabled
REST_REQUIRE_API_KEYfalseapi.rest → requireApiKey
DATA_RETENTION_ENABLEDtruedata.retention → enabled
DATA_RETENTION_CRON15 4 * * *data.retention → cron
WEBHOOK_DELIVERY_RETENTION_DAYS30data.retention → webhookDeliveryDays
DATA_RETENTION_BATCH_SIZE500data.retention → batchSize
DATA_RETENTION_MAX_BATCHES20data.retention → maxBatchesPerRun
REALTIME_RETENTION_ENABLEDtruerealtime.retention → enabled
REALTIME_OUTBOX_COMPLETED_DAYS7realtime.retention → completedDays
REALTIME_OUTBOX_FAILED_DAYS30realtime.retention → failedDays
JOB_RUN_RETENTION_DAYS30data.retention → jobRunDays
AUTH_AUDIT_LOG_RETENTION_DAYS90data.retention → authAuditLogDays
LOG_PERSISTENCE_ENABLEDtruelogs.retention → enabled (any value but false is true). Off keeps the in-memory buffer only
LOG_RETENTION_DAYS7logs.retention → retentionDays
LOG_MAX_ROWS2000000logs.retention → maxRows, a ceiling regardless of age. 0 disables the cap
AUTH_SITE_URLemptyauth.urls → siteUrl, the fallback redirect for an auth flow
AUTH_REDIRECT_URLSemptyauth.urls → redirectUrls, comma or whitespace separated, each with a scheme

REST_MAX_ROWS, REST_RPC_SECURITY_DEFINER and EDGE_FUNCTION_LOG_RETENTION_DAYS above work the same way. Every field, with its full description, is in the settings reference.


Push notifications

Firebase

VariableDescription
FIREBASE_PROJECT_IDFirebase project ID
FIREBASE_CLIENT_EMAILService account client email
FIREBASE_PRIVATE_KEYService account private key, PEM with \n newlines

APNs

VariableDefaultDescription
APNS_KEYempty.p8 private key contents
APNS_KEY_IDemptyApple Developer key ID
APNS_TEAM_IDemptyApple Developer Team ID
APNS_BUNDLE_IDemptyApp bundle identifier
APNS_PRODUCTIONfalsetrue for production APNs gateway

Project Studio and admin

VariableDefaultDescription
CONSOLE_SQL_ENABLEDfalseEnable the raw SQL editor in Project Studio
CONSOLE_LOG_BUFFER_SIZE500Number of log lines kept in memory
CONFIG_ENCRYPTION_KEYemptyEncrypts Studio-managed secrets at rest. Base64, 32 bytes. Absent is allowed — the server boots and non-secret settings work, but saving a secret is refused with a clear message

Edge functions

VariableDefaultDescription
EDGE_PUBLIC_INVOKE_ENABLEDfalseAllow anonymous /api/edge/:route invocation
EDGE_INVOKE_TOKENemptyOptional server-to-server token for x-motherbase-edge-token
EDGE_SECRETS_ENABLEDtrueInject Studio-managed edge secrets into a function's env. Set to false to withhold them; Shield reports when it is on and escalates to critical alongside EDGE_PUBLIC_INVOKE_ENABLED
EDGE_MAX_MEMORY_MB512Ceiling for a function's memory limit, enforced on the worker process (default per function: 128)
EDGE_MAX_TIMEOUT_MS60000Ceiling for a function's wall-clock timeout (default per function: 5000)
EDGE_FUNCTION_LOG_RETENTION_DAYS30Seeds data.retention → edgeFunctionLogDays

The runtime sidecar

Functions execute in a separate Deno service (deploy/edge-runtime), reached over HTTP. These configure the API's half of that link.

VariableDefaultDescription
EDGE_RUNTIME_URLhttp://edge-runtime:9000Where the API reaches the sidecar
EDGE_RUNTIME_TOKENemptyShared secret, sent as x-edge-runtime-token. Must match the one the sidecar was started with
EDGE_RUNTIME_CALLBACK_URLhttp://api:5001This API as the sidecar reaches it, for the sendMail() callback

And these are read by the sidecar process itself, not by the API:

VariableDefaultDescription
EDGE_RUNTIME_PORT9000Port the sidecar listens on
EDGE_ALLOWED_INTERNAL_ORIGINSemptyComma-separated exact origins (http://api:5001) a function's fetch may call despite resolving to a private address. Matched on scheme + host + port, never by suffix
EDGE_MAX_WORKERS16Soft cap on cached worker processes. A burst goes over it rather than failing a call
EDGE_WORKER_IDLE_TTL_MS300000Idle time before a worker is retired
EDGE_WORKER_MAX_INVOCATIONS500Calls a worker serves before it is recycled
EDGE_WORKER_BASE_RSS_MB96Allowance above a function's memory limit for Deno itself, used by the resident-size watchdog. A calibration knob, not a precise number
DENO_DIR/tmp/deno-dirModule cache directory for the worker processes

See edge runtime for what each one changes.


AI assistant

VariableDefaultDescription
AI_DATA_DIRdataDirectory for the assistant's SQLite thread store (assistant.db). Kept separate from BACKUP_DIR so restoring a database backup never touches conversations.
AI_ASSISTANT_ENABLEDfalseTurn the assistant on with the environment-provided key
AI_PROVIDERemptyProvider id, one of the ids @motherbase/ai declares
AI_MODELemptyModel id for that provider
AI_API_KEYemptyThe key. Nothing is configured until both this and AI_PROVIDER are set
AI_BASE_URLemptyOverride the provider's base URL (a gateway, a self-hosted endpoint)

These five are the organization-wide default, shaped exactly like a saved ai.assistant settings group and reported to Studio as source: 'org'. A project that saves its own ai.assistant overrides them entirely. A half-filled environment — a provider with no key, or a key with no provider — is treated as not configured rather than as a broken configuration. In a standalone deployment with no Platform Console, these are how an operator configures the assistant. See AI assistant.


Platform Console

Only needed when running the multi-project Platform Console.

VariableDefaultDescription
CONSOLE_MODEproddev or prod
PLATFORM_SERVICE_TOKENgenerated by setupShared secret for Console to project API calls
PROJECTS_DIRdev: .dev-projects, prod: /srv/projectsDirectory where project stacks are stored
COMPOSE_FILEmode-specific deploy fileOptional project Compose file override
TRAEFIK_NETWORKtraefik_proxyDocker network used by Traefik
UPDATE_CHECK_URLemptyOptional release-channel URL used by the Console update status card
CONSOLE_PORT5500Console HTTP port
CONSOLE_JWT_SECRETdev fallback onlySigns Console operator sessions. Required in prod mode, minimum 32 characters — the Console refuses to boot otherwise
CONSOLE_ADMIN_EMAILadmin@example.comSeeded first owner
CONSOLE_ADMIN_PASSWORD_HASH_B64emptyBase64 of the owner's bcrypt hash. Base64 because Compose mangles $ through ${} interpolation
CONSOLE_ADMIN_PASSWORD_HASHemptyRaw hash fallback for non-Compose setups
CONSOLE_ADMIN_PASSWORD_TEMPORARYfalseForce a password change on first login
CONSOLE_BASE_URL<scheme>://localhost:<port>Public base URL trusted for auth
CONSOLE_TRUSTED_ORIGINSemptyExtra comma-separated origins to trust (proxies that rewrite Host)
CONSOLE_RATE_LIMIT_WINDOW_MIN15Console auth rate-limit window
CONSOLE_RATE_LIMIT_AUTH_MAX20Console auth attempts per window
CONSOLE_RATE_LIMIT_PROXY_MAX600Requests per window through the Console project proxy
CONSOLE_AUDIT_MAX_ENTRIES10000Audit log retention, clamped to [1, 1000000]
CONSOLE_BILLING_ENABLEDfalseShow the Billing tab
CONSOLE_STACK_COMPOSEdeploy/console/docker-compose.ymlStack the self-update targets
CONSOLE_STACK_SERVICESconsole orchestratorServices in that stack
PLATFORM_DOMAIN_SUFFIXdev: localhostBase domain new project hostnames are derived from
API_IMAGEmotherbase-api:1.0.0Image used for new project stacks
CERT_RESOLVERletsencryptTraefik certificate resolver for new projects
ORCHESTRATION_URLhttp://orchestrator:5501Orchestration daemon address
ORCHESTRATION_SECRET—Shared bearer secret, minimum 32 characters
ORCHESTRATION_PORT5501Port the daemon listens on
PREVIEW_ENABLEDfalsePreview projects per pull request. Needs PREVIEW_GITHUB_SECRET set as well, or it stays off
PREVIEW_GITHUB_SECRETemptySecret the GitHub webhook is signed with
PREVIEW_GITHUB_TOKENemptyToken used to post the preview URL back on the PR
PREVIEW_REPO_ALLOWLISTemptyComma-separated owner/repo list allowed to open previews
PREVIEW_TTL_HOURS72Hours a preview stack lives before it is reaped
DB_PORTallocatedHost port for a project's Postgres, assigned by the Console from 54320-54419 and written into the project's .env.prod. Not something you set by hand

UPDATE_CHECK_URL may return { "version": "1.2.3" }, { "tags": [...] }, or a Docker Hub-style { "results": [{ "name": "1.2.3" }] } payload. The Console only checks and displays availability; updates still use the supported setup path. See Updates.

setup.sh and the Console deployment files also use install-time values such as DOMAIN, CONSOLE_DOMAIN, ACME_EMAIL, and Traefik DNS challenge credentials.


CLI

Used by @motherbase/cli for non-interactive runs and CI.

VariableDescription
MOTHERBASE_CONSOLE_URLPlatform Console URL for Console mode
MOTHERBASE_TOKENConsole session token, equivalent to --token
MOTHERBASE_PROJECTProject id/name to operate through the Console proxy
MOTHERBASE_URLDirect project API URL for single-project direct mode
MOTHERBASE_ADMIN_TOKENAdmin bearer token for direct project mode
MOTHERBASE_DEBUG1 logs one line per HTTP request to stderr, equivalent to --debug. Never the request body

SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD are read by the project seed script (npm run db:seed), not by the running server.

Console mode uses MOTHERBASE_CONSOLE_URL + MOTHERBASE_TOKEN + MOTHERBASE_PROJECT. Direct mode uses MOTHERBASE_URL + MOTHERBASE_ADMIN_TOKEN.