Skip to content

Environment

Every variable with its default is listed on the generated environment variables reference. This page explains how the values travel and which groups matter for an operator.

Container Mechanism Source
api (platform) init.sh runs docker/envs/envs.php: each LARAVEL_<NAME> variable is written to .env as <NAME>=value; Laravel reads .env api/docker/envs/envs.php
api (tenants) ulams:tenant:create and ulams:tenant:sync-env write .env.<tenant API host>: a copy of the platform .env with the tenant’s values on top (database, bucket, keys, URLs, Redis prefix) packages/tenancy/src/Support/TenantNaming.php
api (scripts) Read directly by init.sh and the worker scripts: JWT_*_KEY_BASE64, DISABLE_*, MULTI_DOMAINS, QUEUE_IDLE_SLEEP, LONG_JOB_* api/init.sh, api/queue.sh
h5p Its own variables, plus each tenant’s database, bucket and token from the Laravel env files (ENV_DIR) api/h5p/src/config.ts
pdf Its own variables (PDF_*) api/pdf/README.md
web ULAMS_* variables read when the server starts front/web/.env.example
admin, front REACT_APP_* / VITE_APP_* variables injected into index.html on every page load by a small PHP script admin/config/php/index.php

Changing a LARAVEL_* value needs a restart of api (the file is rewritten on start); tenant env files pick up platform changes on the next ulams:tenant:sync-env, which also runs on start. Tenants inherit the platform AI settings (ANTHROPIC_API_KEY, AI_DRIVER, AI_MODEL_*) this way, unless a tenant overrides them with ulams:tenant:set-env (see Tenants). The H5P service re-reads the env files every TENANT_RELOAD_CHECK_MS (2 s).

Category Set where Main variables
Application api APP_KEY, APP_URL, APP_ENV=production, APP_DEBUG=false
Platform keys api JWT_PRIVATE_KEY_BASE64, JWT_PUBLIC_KEY_BASE64
Database api DB_*; DB_ADMIN_* for the role that creates tenant databases
Redis protocol api, h5p REDIS_HOST, REDIS_PORT, REDIS_PASSWORD; prefixes are set per tenant
Object storage api, h5p FILESYSTEM_DRIVER=s3, AWS_*; TENANCY_S3_* for bucket creation; S3_* on h5p
Tenancy api TENANCY_* host patterns, TENANCY_PLATFORM_HOSTS, TENANCY_PLATFORM_API (off by default), TENANT_DEMO_PASSWORD
Content origin api CONTENT_ORIGIN (platform), TENANCY_CONTENT_HOST (tenants; {slug}.content.<app domain> for the same-site mode)
Cookies and origins api, web SESSION_COOKIE_PREFIX, TRUSTED_ORIGINS, ORIGIN_CHECK (api); ULAMS_COOKIE_SECURE, ULAMS_COOKIE_FALLBACK_PREFIX (web)
Services api, h5p, pdf H5P_SERVICE_URL, H5P_INTERNAL_TOKEN, PDF_SERVICE_URL, PDF_INTERNAL_TOKEN
E-mail api MAIL_*, MJML_API_URL
First admin api INITIAL_USER_EMAIL, INITIAL_USER_PASSWORD
Uploads api UPLOADS_* size and archive limits, UPLOADS_SCANNER
LTI api LTI_* (issuer defaults to APP_URL; full list in LTI)
Error tracking api, admin SENTRY_LARAVEL_DSN or SENTRY_DSN, SENTRY_ENVIRONMENT; REACT_APP_SENTRYDSN
Process control api DISABLE_PHP_FPM, DISABLE_HORIZON, DISABLE_QUEUE, DISABLE_SCHEDULER, DISABLE_DB_MIGRATE, DISABLE_DB_SEED, DISABLE_TENANT_SYNC
Content Security Policy web, api, proxy CSP_ENFORCE, ULAMS_CONTENT_ORIGIN, ULAMS_STORAGE_ORIGINS (web); CSP_REPORT_RETENTION_DAYS (api); ULAMS_CSP_HEADER (admin policy in the proxy). See Security headers
Front routing web, admin ULAMS_TENANT_HOSTS, ULAMS_PLATFORM_HOSTS, REACT_APP_TENANT_API_HOST_PATTERN
Platform landing web ULAMS_LANDING_STATUS (final or actual, see below)
Queues and workers api (container) BUILDER_QUEUE_RETRY_AFTER (api, default 2400); QUEUE_CONNECTION, WORKERS_CHECK_INTERVAL, WORKERS_MAX_TIME, COURSE_BUILDER_QUEUE(_CONNECTION), LONG_JOB_QUEUE_CONNECTION and LONG_JOB_QUEUE (worker script and config/queue.php; the old LONG_JOB_CONNECTION is still accepted). See Queues and the scheduler
Scheduler api TENANCY_SCHEDULER_LOCK (default true: one replica runs each minute, ADR 0068). See Queues and the scheduler
Demo mode tenant env file DEMO_MODE (default false), DEMO_ADMIN_EMAIL, DEMO_STUDENT_EMAIL, DEMO_TUTOR_EMAIL, DEMO_ADMIN_URL, DEMO_RESET_SCHEDULE (true), DEMO_RESET_CRON (0 * * * *), DEMO_RESET_WIPE_FILES (false), DEMO_RESET_STUDENTS (5), ULAMS_DEMO_EXPERIENCE. Set DEMO_MODE with ulams:tenant:create <slug> --demo=on, never on the platform. See Demo mode
cmi5 api CMI5_DISK (follows SCORM_DISK), CMI5_SESSION_MINUTES (default 120). See cmi5 packages
Adapt sources api ADAPT_SOURCE_ENABLED (default false), ADAPT_BUILDER_URL (http://adapt-builder:8080), ADAPT_BUILDER_TOKEN, ADAPT_BUILDER_TIMEOUT (300), ADAPT_MAX_SOURCE_KB (4096). See Adapt

Do not set tenant-only values on the platform: TENANT_SLUG, REDIS_PREFIX, CACHE_PREFIX, HORIZON_PREFIX, FRONTEND_URL, ADMIN_URL and DEMO_MODE are written into each tenant’s file.

A tenant’s .env.<host> file is generated, never edited by hand. Its values come from three layers, lowest first:

  1. The platform environment (LARAVEL_* written to the platform .env, or plain variables). ulams:tenant:create and ulams:tenant:sync-env start every tenant file from a copy of the platform .env.

  2. Inherited settings. For the keys listed in ulams_tenancy.inherited_env, the platform’s non-empty value is written into every tenant file: ANTHROPIC_API_KEY (the legacy spelling ANTROPHIC_API_KEY is accepted), ANTHROPIC_BASE_URL, AI_DRIVER, AI_MODEL_DEFAULT, AI_MODEL_LIGHT, AI_MODEL_PREMIUM and their _LABEL variants. A changed platform value reaches the tenants on the next ulams:tenant:sync-env, which also runs when the api container starts.

  3. Per-tenant overrides, which win over layer 2 and over the platform. They are stored encrypted in the tenants table and written into the tenant file on every sync:

    Terminal window
    php artisan ulams:tenant:set-env coffee --set=AI_DRIVER=disabled --set=AI_MODEL_DEFAULT=<model>
    php artisan ulams:tenant:set-env coffee --unset=AI_DRIVER # inherit the platform value again

    Only the inheritable keys are accepted; any other key is refused, so the command cannot change a database, bucket or key setting. It prints key names, never values. Run it on the platform, without --domain. The same is available through the platform API and ulams tenants set-env. See Tenants and ADR 0063.

The tenant’s own values (database, bucket, keys, URLs, Redis prefix, DEMO_MODE, CONTENT_ORIGIN) come from the tenants table and replace the platform’s.

ULAMS_LANDING_STATUS is read by the web container when it starts. It only changes what the platform landing (the product page on the platform host, not a tenant’s site) shows for roadmap items:

Value Effect
final (default; any other or empty value counts as final) Every roadmap item reads as delivered: no Coming or Preview badges, no roadmap captions, and ulams’s own Coming and Partial cells in the comparison table read Yes
actual The honest status kept in the data files; competitor cells never change

The API keeps the long-form notes; this site does not repeat them: