Environment Variables
bext reads environment variables at startup. A curated set of BEXT_* variables override the matching value in bext.config.toml, giving you a clean way to inject secrets and per-environment configuration without editing config files — the 12-factor pattern that container and PaaS deployments expect.
Precedence#
Configuration values are resolved in this order (highest priority first):
- Environment variables (e.g.
BEXT_LISTEN,PORT) - Config file (
bext.config.toml) - Defaults (built into the binary)
An environment variable always overrides the matching config-file value.
In masquerade mode bext derives its listeners and TLS from your nginx vhosts, so BEXT_LISTEN and BEXT_TLS_* have no effect there. Use them only in standalone bext run / bext serve deployments.
Scope#
Overrides apply when bext loads a bext.config.toml for the standalone server (bext run, bext serve, and on SIGHUP reload). In masquerade mode bext derives its listeners and TLS from your nginx vhosts, so BEXT_LISTEN / BEXT_TLS_* do not apply there.
Each row in the tables below notes the bext.config.toml field it overrides. Variables without an "overrides …" note (DATABASE_URL, REDIS_URL, BEXT_LICENSE_KEY, BEXT_STORAGE_*, BEXT_CLOUD_*, DOTENV_PATH, NODE_ENV, RUST_LOG) are consumed directly rather than layered over a config field.
Variable Reference#
Server#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_CONFIG |
Path to the config file | bext.config.toml |
/etc/bext/bext.config.toml |
BEXT_LISTEN |
Listen address (host:port); overrides [server] listen |
0.0.0.0:3061 |
127.0.0.1:8080 |
PORT |
Listen port only — keeps the configured host, swaps the port (PaaS convention; BEXT_LISTEN wins if both are set) |
3061 |
3000 |
BEXT_STATIC_DIR |
Static assets directory; overrides [server] static_dir |
(none) | /var/www/static |
BEXT_MAX_BODY_SIZE_MB |
Maximum request body size, in megabytes; overrides [waf] max_body_size_bytes and the payload ceiling |
10 |
50 |
BEXT_WAF_CRAWLER_RPM |
Built-in global throttle for aggressive non-search crawlers (one shared per-minute budget; search engines + real users never matched). 0 disables. See Security |
0 |
30 |
Rendering / SSR#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_BUNDLE_PATH |
Path to the SSR bundle; overrides [render] bundle_path |
(none) | /app/dist/server/index.js |
BEXT_RENDER_WORKERS |
Number of V8 SSR worker isolates; overrides [render] workers |
4 |
8 |
BEXT_V8_POOL |
Use the multiplexed render-worker pool (responses demuxed by request id) instead of the legacy single-worker client. The serialized client caps a worker at one in-flight render, idling the render thread between IPC round-trips — enabling the pool measured ~7× higher single-worker throughput. On by default under bext run. 0 reverts |
1 (bext run) |
1 |
BEXT_V8_POOL_SIZE |
Render workers the pool spawns (clamped 1–16); under bext run it tracks [render] workers. One worker already saturates the master on IO/master-bound routes; scale up for CPU-heavy SSR (the pool scales ~linearly there) |
min(workers, 16) |
8 |
BEXT_V8_SHM_IPC |
Return the render response body through shared memory rather than the worker IPC socket — saves a socket write+read per request (larger bodies benefit most). On by default under bext run. 0 reverts |
1 (bext run) |
1 |
BEXT_V8_ASYNC_EVAL |
Await the render pool directly on the HTTP worker instead of parking a spawn_blocking thread on the IPC round-trip. Frees the worker while the render is in flight and halves per-request context switches, so the multiplexed pool scales past a single worker (a blocking pool regresses). Covers buffered /api/* handlers and page renders. Requires the pool (BEXT_V8_POOL=1). On by default under bext run. 0 reverts |
1 (bext run) |
1 |
TLS#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_TLS_CERT |
Path to TLS certificate (PEM); overrides [tls] cert |
(none) | /etc/ssl/certs/domain.pem |
BEXT_TLS_KEY |
Path to TLS private key (PEM); overrides [tls] key |
(none) | /etc/ssl/private/domain.key |
BEXT_ACME_EMAIL |
Email for ACME account registration; overrides [tls] acme_email |
(none) | admin@example.com |
Cache#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_CACHE_MAX_ENTRIES |
Maximum ISR cache entries; overrides [cache.isr] max_entries |
10000 |
50000 |
BEXT_CACHE_TTL_MS |
Default ISR cache TTL in milliseconds; overrides [cache.isr] default_ttl_ms |
60000 |
300000 |
BEXT_CACHE_SWR_MS |
Default stale-while-revalidate window (ms); overrides [cache.isr] default_swr_ms |
3600000 |
86400000 |
Database#
| Variable | Description | Default | Example |
|---|---|---|---|
DATABASE_URL |
PostgreSQL connection URL (used when [database] url is unset) |
(none) | postgres://user:pass@db:5432/bext |
BEXT_DB_POOL_SIZE |
Max database connections; overrides [database] max_connections |
(pool default) | 25 |
Redis#
| Variable | Description | Default | Example |
|---|---|---|---|
REDIS_URL |
Redis connection URL for L2 cache and distributed features (used when [redis] url is unset) |
(none) | redis://redis.internal:6379/0 |
Logging#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_LOG_LEVEL |
Simple log level applied when RUST_LOG is unset (error/warn/info/debug/trace) |
info |
debug |
RUST_LOG |
Fine-grained log filter (standard tracing syntax); takes precedence over BEXT_LOG_LEVEL |
info |
warn,bext_server=debug |
License#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_LICENSE_KEY |
License key for Pro or Enterprise features | (none) | BEXT-PRO-eyJ0aWVy... |
Storage & Backups#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_STORAGE_PROVIDER |
Object storage backend | local |
s3, r2 |
BEXT_STORAGE_BUCKET |
S3/R2 bucket name | (none) | my-bext-backups |
BEXT_STORAGE_REGION |
AWS region (ignored for R2) | us-east-1 |
eu-west-1 |
BEXT_STORAGE_ENDPOINT |
Custom endpoint (required for R2/MinIO) | (none) | https://ACCT.r2.cloudflarestorage.com |
BEXT_STORAGE_ACCESS_KEY |
S3/R2 access key | (none) | AKIA... |
BEXT_STORAGE_SECRET_KEY |
S3/R2 secret key | (none) | wJalr... |
Cloud Sync (nginx)#
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_CLOUD_URL |
bext cloud API URL | http://localhost:3025 |
https://cloud.bext.dev |
BEXT_CLOUD_API_KEY |
API key for CLI-to-cloud auth | (none) | my-secret-key |
PRISM performance & live-reload#
Shipped 2026-06-12. These default to the production-proven values; set to 0
to opt out. They apply to the PRISM/turbopack + V8 stack.
| Variable | Description | Default | Example |
|---|---|---|---|
BEXT_TURBOPACK_DISK_CACHE |
Persistent on-disk compiled-bundle cache — survives restart, so a redeploy serves unchanged routes from disk ( |
on | 0 |
BEXT_TURBOPACK_DISK_CACHE_DIR |
Disk compile-cache directory | /var/cache/bext/compile |
/mnt/cache/compile |
BEXT_TURBOPACK_DISK_CACHE_MAX_ENTRIES |
Disk compile-cache entry cap (oldest pruned) | 4096 |
8192 |
BEXT_V8_BYTECODE_DISK_CACHE |
Persistent V8 bytecode (code-cache) on disk — post-restart context builds restore via ConsumeCodeCache instead of re-parsing |
on | 0 |
BEXT_V8_BYTECODE_DISK_CACHE_DIR |
Bytecode-cache directory | /var/cache/bext/bytecode |
/mnt/cache/bytecode |
BEXT_MASQUERADE_ROUTE_WATCH |
Master per-site route watcher — new routes & edits go live ~2 s with no restart/purge | on | 0 |
BEXT_MASQUERADE_ROUTE_WATCH_MS |
Route-watcher poll interval (ms) | 2000 |
1000 |
BEXT_WORKER_CONSOLE_LOG |
Mirror worker console.* to per-site files (also served via GET /__bext/sdk/logs?site=<s>) |
on | 0 |
BEXT_WORKER_CONSOLE_DIR |
Per-site worker console-log directory | /var/log/bext/console |
/var/log/bext-console |
BEXT_PRISM_SOURCEMAPS |
Emit + compose source maps for PRISM renders so V8 stack traces map to original TSX in the dev overlay (dev only; pair with BEXT_TURBOPACK_DISK_CACHE=0) |
off | 1 |
BEXT_TURBOPACK_COMPILE_SINGLEFLIGHT |
Dedup concurrent compiles of the same module | on | 0 |
BEXT_TURBOPACK_PIPE_POOL_SIZE |
tsc-rs --pipe worker pool size |
min(cores, 4) |
8 |
For the V8-pool resilience flags (circuit breaker, render deadline, streaming lanes, etc.) see V8 Render Engine and V8 Render Pool.
Miscellaneous#
| Variable | Description | Default | Example |
|---|---|---|---|
DOTENV_PATH |
Path to .env file |
.env |
/etc/bext/.env.production |
BEXT_UPGRADE_FDS |
Socket FDs for zero-downtime upgrade (set automatically) | (none) | 3,4 |
BEXT_UPGRADE_OLD_PID |
Old process PID during upgrade (set automatically) | (none) | 12345 |
NODE_ENV |
Passed through to SSR bundles and plugins | production |
development |
Using .env Files#
bext automatically loads a .env file from the working directory at startup:
# .env
BEXT_LICENSE_KEY=BEXT-PRO-eyJ0aWVy...
DATABASE_URL=postgres://user:pass@db:5432/bext
REDIS_URL=redis://localhost:6379/0
Override the .env file path:
DOTENV_PATH=/etc/bext/.env.production bext-server run
Variables in .env do not override existing OS environment variables. This means you can set secrets in the OS environment and use .env only for non-sensitive defaults.
Security Notes#
- Never commit
.envfiles containing secrets to version control. Add.envto your.gitignore. - Prefer
BEXT_LICENSE_KEYas an environment variable over putting the key inbext.config.toml, especially in containerized environments. - The admin dashboard is enabled via the
[admin] enabled = trueconfig setting (or[endpoints] admin = true), not an environment variable. When disabled, the admin endpoints return 404. See Endpoints & Credentials. - For Kubernetes, use Secrets or an external secret manager (Vault, AWS Secrets Manager) to inject sensitive variables.
Related#
- bext.config.toml — the config fields these variables override
- V8 Render Engine — V8 pool resilience flags (
BEXT_V8_*,BEXT_RENDER_DEADLINE_MS) - Architecture: V8 Render Pool — pool tuning flags referenced from the PRISM table
- Docker — passing variables via
environmentandenv_file - Endpoints & Credentials — admin panel enable/disable (config, not env var)