Self-Hosting Configuration

Environment variables behind the Self-Hosting deployment.

For source development, use Local Setup and the repository's .env.sample. Many values can embed the literal string <subdomain>, which getEnv (utils.ts) replaces with the request tenant at runtime; useful for per-tenant URLs and keys.

Runtime vs build-time

Backend services read everything below from the container environment at startup; restart to apply changes. The Core UI container is different: its entrypoint writes every REACT_APP_* variable into a browser-readable js/env.js at startup, so REACT_APP_* values behave as runtime configuration for erxes/erxes-next-ui even though the same names are baked into the bundle as fallbacks. The standalone apps (client-portal-template, help-center, posclient-front) read NEXT_PUBLIC_* values at their own build or entrypoint time; see their app pages.

Core runtime

VariableRead byRequiredDefaultNotes
NODE_ENVAll backend + UIYesnoneproduction in deployment; development enables dev remotes, router download, and CORS extras
PORTEach serviceNo4000 gateway, 3300 core-api, 3301 logs, 3302 automations, 3303+ pluginsPer-container override
VERSIONcore-api, gateway, service discoveryNoosos or saas; switches /initial-setup, plugin discovery, and charge checks
RELEASE_VERSIONPlugin APIs on registrationNononeValues starting with 3. become the releaseVersion used in plugin UI asset URLs; anything else becomes latest
LOAD_BALANCER_ADDRESSEvery backend serviceRecommended in productionderivedAddress registered in Redis; defaults to http://plugin-<name>-api:<port> in production, http://localhost:<port> in dev
GET_CP_TOKENcore-apiOnly to enable the routenoneShared secret for GET /get-client-portal-token?GET_CP_TOKEN=
TEMPLATE_EXPORT_KEYcore-apiFor template exportnonePassphrase hashed to the AES-256-GCM key that encrypts template exports

Database

VariableRead byRequiredDefaultNotes
MONGO_URLAll backend servicesYesmongodb://127.0.0.1:27017/erxes?directConnection=trueMust reach all replica-set members from the containers; the logs service derives <db>_logs from it
DB_NAMEerxes-api-shared (saas)saas onlyerxes_<organizationId>Tenant database name template
CORE_MONGO_URLbackend/saas-migrations scriptsFor those migrationsnoneSome scripts accept a domain-specific override instead of MONGO_URL

Redis

VariableRead byRequiredDefaultNotes
REDIS_HOSTAll backend servicesYesnoneShared ioredis client, BullMQ queues, GraphQL pubsub
REDIS_PORTAll backend servicesNo6379none
REDIS_PASSWORDAll backend servicesWhen Redis requires authnoneMust match the Redis container's requirepass; there is no REDIS_URL
SKIP_REDISposclient sync utilsNononeWhen set, POS config sync throws token not found instead of creating a config for an unknown token

Redis holds service discovery (erxes-service-*, erxesservice:config:*), session tokens (user_token_*), plugin lists (erxes-active-plugins), and every BullMQ queue. Persist it (--appendonly yes) and use noeviction.

URLs, domains, and CORS

VariableRead byRequiredDefaultNotes
DOMAINgateway, core-api, servicesYeshttp://localhost:3000Public UI origin; also the CORS allow-list anchor and the stored hostname fallback
GATEWAY_URLcore-api email linksNo${DOMAIN}/gatewayPublic gateway prefix used in emails, unsubscribe links, webhooks
WIDGETS_DOMAINgateway, core-apiNohttp://localhost:3200Widgets origin added to CORS
ALLOWED_DOMAINSgateway, core-api, servicesNononeExtra comma-separated CORS origins
ALLOWED_ORIGINSgateway, core-api, servicesNononeComma-separated regexes appended to the CORS origin list
CLIENT_PORTAL_DOMAINSlogs, automations servicesNononeExtra origins for the background services' CORS
TRUST_PROXYAll backend servicesNoloopback, linklocal, uniquelocalExpress trust proxy; widen only if your LB appends client IPs

Auth and sessions

VariableRead byRequiredDefaultNotes
JWT_TOKEN_SECRETgateway, core-api, pluginsYesSECRETSigns and verifies user, portal, and app JWTs. Change it and keep it identical on every backend service
SAME_SITEcore-apiNononeSet to none to emit cross-site auth cookies where the code supports it
WORKOS_API_KEY, WORKOS_PROJECT_IDcore-apiFor WorkOS SSOnoneSSO login mutations
OAUTH_DEVICE_VERIFICATION_URIcore-apiNobuilt-inOAuth device-flow verification page override

Plugin and service selection

VariableRead byRequiredDefaultNotes
ENABLED_PLUGINSgateway, core-api, dev scriptsFor pluginsnoneComma-separated base names (sales,operation); controls gateway waits and /get-frontend-plugins
ENABLED_PLUGINS_ONLY_APIgateway, dev scriptsNononeBackend-only plugins without frontend remotes
ENABLED_SERVICESscripts/start-api-dev.jsDev onlynoneautomations,logs → *-service Nx projects; not read by Docker images
MAX_PLUGIN_RETRYgatewayNounlimitedBounds plugin-join and /graphql readiness retries at startup

Gateway tuning

VariableRead byRequiredDefaultNotes
APOLLO_ROUTER_PORTgatewayNo50000Loopback port for the internal router
INTROSPECTIONgatewayNooff in productiontrue enables GraphQL introspection in production
GRAPHQL_LIMITERgatewayNooffWhen set, applies depth/alias/character limits to /graphql
SUPERGRAPH_POLL_INTERVAL_MSgateway (dev)No10000Dev-mode recompose interval
DEBUG_GATEWAY_AUTHgatewayNofalseVerbose auth, proxy, and limiter request logs

Workers and retention

VariableRead byRequiredDefaultNotes
LOG_RETENTION_DAYSerxes-api-shared logs schemaNo365TTL index on the _logs database logs collection
REVERT_AUTO_JOURNAL_MAXerxes-api-sharedNo1000Max documents snapshotted per bulk write for the undo journal
SEGMENT_CONCURRENCY_CHANGED / _FORGET / _REBUILD / _RECONCILElogs-serviceNo10 / 5 / 1 / 3BullMQ concurrency per segment queue
SEGMENT_RECONCILE_CRON / _TZ / _BATCH / _STEPS, SEGMENT_REBUILD_PAGE, SEGMENT_PREVIEW_BUDGET_MS, SEGMENT_TIME_ZONEsegment engineNobuilt-inSegment sweep tuning
TIMEZONEsales board/POS report queriesNo0 (hour offset) / UTCNumeric hour offset in sales date math

Email

VariableRead byRequiredDefaultNotes
SENDGRID_API_KEYcore-api auth emailsFor SendGridnoneThrows SENDGRID_API_KEY is missing in environment when a SendGrid path runs without it
SENDGRID_WEBHOOK_PUBLIC_KEYcore-api broadcast trackersFor SendGrid event webhooksnoneVerifies inbound event signatures; a deployment-level key marks the webhook shared
AWS_SES_ACCESS_KEY_ID, AWS_SES_SECRET_ACCESS_KEY, AWS_SES_CONFIG_SET, AWS_REGIONbroadcast transporterFor SESnoneSES sending path
DEFAULT_FROM_EMAILerxes-api-shared emailNononeFallback sender; COMPANY_EMAIL_FROM system config overrides per tenant
EMAIL_DISPOSABLE_DOMAINScore-api intakeNononeExtra disposable-domain list
EMAIL_PROVEN_WINDOW_DAYScore-apiNo180Proven-address window
EMAIL_RAMP_TIERS, EMAIL_SOFT_BOUNCE_LIMITcore-api broadcastNobuilt-in tiers / 3Sending ramp and bounce suppression
MAIL_DOMAIN, MAIL_WORKER_URL, MAIL_WEBHOOK_SECRET, MAIL_TENANT, MAIL_SENDING_ACCOUNT_ID, MAIL_SENDING_API_TOKEN, MAIL_SENDING_DAILY_LIMIT, MAIL_INBOUND_RATE_LIMIT, MAIL_RECEIVE_URLfrontline_api mail channelFor the mail integrationnoneCloudflare Email Routing → worker → /mail/receive; see .env.sample and the frontline plugin guide

Frontend runtime (REACT_APP_*)

Set on the core-ui container; injected into window.env at startup and falling back to build-time values:

VariableDefaultNotes
REACT_APP_API_URL<proto>//<host>/gateway (prod), http://localhost:4000 (dev)Public gateway URL the browser calls; <subdomain> in the value is replaced by the hostname's first label
REACT_APP_IMAGE_CDN_URLnoneImage CDN base
REACT_APP_GOOGLE_MAP_API_KEYnoneMaps integration
REACT_APP_HIDE_CORE_MODULESnoneHides built-in navigation modules
REACT_APP_SENTRY_DSN, REACT_APP_SENTRY_ENVIRONMENTnoneFrontend Sentry

Variables on the standalone apps include NEXT_PUBLIC_ERXES_API_URL (client-portal-template, help-center; the latter injects it into env.js via its own docker-entrypoint.sh), NEXT_PUBLIC_ERXES_CP_TOKEN (client-portal-template only), and NEXT_PUBLIC_MAIN_API_DOMAIN, NEXT_PUBLIC_APP_VERSION (posclient-front). Check each app's Dockerfile/entrypoint: a container env override only works where the app injects runtime env; otherwise rebuild after changing them.

Observability

VariableRead byRequiredDefaultNotes
SENTRY_DSN, SENTRY_ENVIRONMENT, SENTRY_RELEASE, SENTRY_SERVER_NAME, SENTRY_TRACES_SAMPLE_RATEAll backend servicesNooffSentry error tracing; tag as plugin/service

Not env vars

Storage and upload settings are system configs stored in the database, read via configs.getConfigs: UPLOAD_SERVICE_TYPE, AWS_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_COMPATIBLE_SERVICE_ENDPOINT, AWS_FORCE_PATH_STYLE, AWS_PREFIX, GOOGLE_CLOUD_STORAGE_BUCKET, GOOGLE_APPLICATION_CREDENTIALS, GOOGLE_PROJECT_ID, CLOUDFLARE_*, AZURE_STORAGE_*, FILE_SYSTEM_PUBLIC. Set them in Settings → file upload, not in .env.

Was this helpful?