Skip to content
BuildBase

Configuration

All environment variables and Docker images for self-hosted BuildBase.

Environment Variables

Installation (Required)

These are provided by the BuildBase dashboard when you create an Installation.

VariableDescription
INSTALLATION_API_KEYInstallation API key (from dashboard)
INSTALLATION_IDInstallation ID (from dashboard)

Server (Required)

VariableDescription
NODE_ENVSet to production
PORTServer port (default: 3000)
MONGO_CONNECTION_URLMongoDB connection string
REDIS_HOSTRedis hostname (default: localhost)
REDIS_PORTRedis port (default: 6379)
SERVER_URLPublic URL of the tenant server. Enterprise single sign-on needs it public and on HTTPS: the WorkOS redirect URI is built from it (default: http://localhost:4101)
APPLICATION_URLPublic URL of the client app (default: http://localhost:4100)
AUTH_SERVER_URLPublic URL of the auth portal (default: http://localhost:4103)
JWT_PASSJWT signing secret
DB_ENCRYPTION_KEYDatabase field encryption key
SECRET_KEYGeneral application secret
OAUTH2_SECRETOAuth2 token secret
INSTALLATION_API_KEYInstallation API key (from BuildBase dashboard)
INSTALLATION_IDInstallation ID (from BuildBase dashboard)

Client App

VariableDescription
NEXT_PUBLIC_SERVER_URLTenant server URL
NEXT_PUBLIC_DEFAULT_TENANT_SERVER_URLDefault tenant server URL
NEXT_PUBLIC_INSTALLATION_IDInstallation ID (for org filtering)

CORS

VariableDescription
CORS_WHITELISTED_DOMAINSComma-separated list of allowed origins

The platform origins (console.buildbase.app) are always allowed by default.

Redis

VariableDescriptionDefault
REDIS_PASSWORDPassword (if required)—
REDIS_DBDatabase number0

Optional Services

VariableDescription
TRUST_PROXYHow many proxies sit in front of the server and append to X-Forwarded-For. Defaults to 0 for self-hosted, which is right when the compose file publishes the port directly. The production compose sets 1 for its bundled nginx. Set it to the number of appending proxies if you put nginx, a load balancer or Cloudflare in front - too high and every per-IP rate limit is bypassable by a header the caller writes.
GOOGLE_AUTH_CLIENT_IDGoogle OAuth client ID (for Google vendor + Gmail sender)
GOOGLE_AUTH_CLIENT_SECRETGoogle OAuth client secret
GOOGLE_STORAGE_ASSETS_BUCKET_NAMEGCS bucket for file uploads
GOOGLE_STORAGE_STAGING_BUCKET_NAMEGCS bucket browsers upload to directly, before the server checks a file and makes it an asset. Turns on direct uploads up to 100 MB, file fields on forms and uploads by your app's users through the SDK. Unset, every upload goes through the server at 5 MB at most, and form file fields and SDK uploads answer 501. Its own bucket, in the assets bucket's location: soft delete off, a one-day delete lifecycle rule, CORS allowing PUT from any origin with the signed headers, and credentials that can sign URLs (a key file, or iam.serviceAccounts.signBlob on the service account)
GOOGLE_STORAGE_FINALIZE_TOKENShared secret in the Pub/Sub push URL for the staging bucket's finalize notifications (/api/webhooks/storage/finalize?token=...). Optional: unset answers 404 there, and an upload still completes through its own complete call or the 15-minute sweep, an hour later for a browser that closed after its upload
GOOGLE_STORAGE_USAGE_LOG_BUCKETGCS bucket the assets bucket writes its usage logs to. The hourly bandwidth job reads them to measure what each organization serves. Unset: bandwidth is not measured
GOOGLE_STORAGE_USAGE_LOG_PREFIXThe log object prefix the assets bucket was given (--log-object-prefix). Must match, or the bandwidth job finds no logs (default: assets)
EXPORTS_BUCKETGCS bucket an organization owner's data export is written to and downloaded from. Unset, exports are off: the export routes answer 503 and nothing is written to another bucket. Its own bucket, in the assets bucket's location: a 7-day delete lifecycle rule, no retention policy, public access prevention, and credentials that can create, read, list and delete objects in it
TENANT_STORAGE_GATEThe storage gate: off, shadow (log what it would refuse) or true. Self-hosted storage has no ceiling, so here it decides only whether a JSON body carrying a data: file over 16 KB is refused. Unset follows TENANT_FEATURE_GATE, which is off unless set
TENANT_STORAGE_BILLINGStorage billing: off, shadow or true. No effect on a self-hosted install, where storage is not billed (default: off)
TENANT_STORAGE_INTEGRITYThe weekly check of each organization's stored files against its asset records. Unset or shadow logs what it finds and changes nothing; true also sets aside a file no record accounts for (older than a day) and deletes it 7 days later if still unclaimed; off skips the check (default: shadow)
TENANT_STORAGE_PURGEDeleting an organization's files 30 days after its subscription lapsed: off, shadow (log what would be deleted) or true. A self-hosted organization never counts as lapsed, so in practice this has no effect here (default: off)
GOOGLE_WEB_RISK_API_KEYGoogle Web Risk API key for checking redirect destinations. Unset disables reputation checks; links still work.
MAIL_TRACKING_SECRETSigning key for email open/click tracking links. Derived from JWT_PASS when unset; set it to make the tracking key fully independent of the auth key.
URL_SAFETY_LOG_ONLYRecord URL reputation verdicts without acting on them. Defaults to false, so setting GOOGLE_WEB_RISK_API_KEY starts refusing malicious destinations straight away. Set this to true first to measure the false-positive rate before enforcing. (default: false)
URL_SAFETY_ENABLEDMaster switch for URL reputation checks. Only the literal "false" turns them off, and every lookup then returns UNKNOWN. (default: true)
URL_SAFETY_CLEAN_TTL_SECONDSHow long a clean verdict may be cached, in seconds, capped at 86400. A threat verdict ignores this and honours the expiry Google returns. (default: 3600)
WEB_RISK_TIMEOUT_MSTimeout for a single Web Risk lookup, in milliseconds. Lookups sit on a background path but must not hang a worker. (default: 5000)
BREACHED_PASSWORD_CHECKRefuse passwords that appear in known data breaches, on register, reset and change. Only the first five characters of the password's SHA-1 hash leave the server (the Pwned Passwords range API). Set it to off when the server cannot reach api.pwnedpasswords.com; otherwise every password waits out a 2-second timeout before it is allowed. (default: on)
TURNSTILE_SITE_KEYCloudflare Turnstile site key for this install's own sign-in domain (AUTH_URL), used when an org turns bot protection on and has not saved keys of its own. Free from the Cloudflare dashboard; list your auth domain as a hostname on the widget. Leave it and TURNSTILE_SECRET_KEY unset and bot protection stays off unless an org adds its own keys.
TURNSTILE_SECRET_KEYThe secret key paired with TURNSTILE_SITE_KEY. Only this server reads it, to check each solved challenge with Cloudflare, so the server needs to reach challenges.cloudflare.com.
SMS_NUMBER_HOURLY_MAXPhone sign-in codes sent to one phone number in an hour, across every org on this install. Counted before any account lookup, so a number with an account and one without run out alike. It guards your orgs' SMS bills, so DISABLE_RATE_LIMIT does not switch it off. (default: 5)
SMS_NUMBER_DAILY_MAXPhone sign-in codes sent to one phone number in a day. (default: 10)
SMS_IP_HOURLY_MAXPhone sign-in codes requested from one IP address in an hour. Stops one visitor cycling through many numbers to run up text message charges (SMS pumping). (default: 20)
SMS_IP_DAILY_MAXPhone sign-in codes requested from one IP address in a day. Each org's own daily cap is a console setting, not an environment variable. (default: 50)
TENANT_MARKETPLACESelling through Stripe Connect (Billing, Selling and Item types), a kill switch. on opens it: non-production environments sell with Stripe test keys only, production may use live keys. off keeps it closed. test is legacy: Stripe test keys only, everywhere. Anything else reads as off (default: off)
AFFILIATES_PAYPALLets affiliate programs pay out by PayPal, from each app owner’s own PayPal Business account. on to allow it; anything else keeps it off (default: off)
GOOGLE_STORAGE_DELIVERABLES_BUCKET_NAMEGoogle Cloud Storage bucket for the files sellers sell, served only through short-lived signed links (turn on public access prevention). Unset, file items answer FILES_NOT_SET_UP
CLAMAV_HOSTHost of a ClamAV daemon (clamd) that scans each seller’s file before it can be sold. Unset, files are not scanned and may be sold only in a non-production environment (or anywhere while the legacy TENANT_MARKETPLACE=test is set)
CLAMAV_PORTPort of the ClamAV daemon (default: 3310)
SHIPPING_EASYPOSTLets an app owner connect their own EasyPost key for shipping labels and tracking. on to allow it; anything else keeps it off (manual shipments only) (default: off)
SHIPPING_SHIPPOLets an app owner connect their own Shippo token for shipping labels and tracking. on to allow it; anything else keeps it off (manual shipments only) (default: off)
NEW_RELIC_LICENSE_KEYNew Relic ingest license key for YOUR OWN account. Unset (default) disables all APM/log telemetry - nothing is collected or sent
NEW_RELIC_APP_NAMEAPM application name shown in New Relic (defaults to buildbase-server-selfhost)
LOG_LEVELServer log verbosity: error, warn, info (default), http, verbose, debug or silly. Also the New Relic ingest throttle - with NEW_RELIC_LICENSE_KEY set, every logged line is an ingested line, so raise this to warn or error to cut volume without redeploying. An unrecognised value is refused at boot and info is used
BULLMQ_DASHBOARD_PASSWORDPassword for the background-job dashboard at /api/admin/bullmq. Unset, it falls back to INSTALLATION_API_KEY and the server warns at every boot; set its own value so the two can be rotated apart (scripts/generate-selfhost-secrets.sh prints one)
NEW_RELIC_BROWSER_ACCOUNT_IDNew Relic account ID for browser monitoring of the client and auth frontends (shared across apps). Unset (default) disables browser monitoring
NEW_RELIC_BROWSER_LICENSE_KEYNew Relic browser license key (NRJS-…, shared across apps) for YOUR OWN account
NEW_RELIC_BROWSER_APP_ID_CLIENTNew Relic browser application ID for the client (console) frontend
NEW_RELIC_BROWSER_APP_ID_AUTHNew Relic browser application ID for the auth frontend

Generating Secrets

All secret values should be unique, random strings. Generate them with:

openssl rand -hex 32

Or generate all required secrets at once:

for i in JWT_PASS DB_ENCRYPTION_KEY SECRET_KEY OAUTH2_SECRET REDIS_PASSWORD BULLMQ_DASHBOARD_PASSWORD; do echo "$i=$(openssl rand -hex 32)"; done

Docker Images

ImageDescriptionPortHealth Check
buildbaseapp/tenant-serverBackend API server3000GET /api/ready
buildbaseapp/clientWeb dashboard (Next.js SSR)3000GET /
buildbaseapp/authAuth portal (Next.js)3000GET /health

All images are multi-arch (linux/amd64, linux/arm64), built on Node.js 22 Alpine. docker pull selects the correct variant for the host automatically. 32-bit ARM (linux/arm/v7) is not supported - see Supported architectures.

Architecture

Loading diagram...

Your end users' accounts and application data stay in your own MongoDB. The installation stays connected to BuildBase: the central server signs your team in to the console, manages your organizations, receives a status check-in every five minutes and is told about plan changes. Visitor IP addresses are sent to IPinfo for location lookups unless IP_INFO_MODE is set to disabled, and uploaded files go to a Google Cloud Storage bucket you own.