Configuration

boxlang.json, the --bx-config flag, environment variable interpolation, and app-level settings.

boxlang.json: global vs. project

BoxLang's CLI only auto-loads one config file: ~/.boxlang/config/boxlang.json, the machine-wide config. A boxlang.json placed next to your entry script is not discovered automatically.

LocationAuto-loaded?Scope
~/.boxlang/config/boxlang.jsonYes, alwaysEvery BoxLang process on the machine
./boxlang.json (project-local)No — requires --bx-configOnly when explicitly passed

Using --bx-config

bashboxlang --bx-config ./boxlang.json app.bxs

Other useful global CLI flags (from boxlang --help):

FlagPurpose
--bx-config <PATH>Use a custom configuration file
--bx-home <PATH>Set the BoxLang runtime home directory
--bx-debugEnable debug mode with startup timing
--bx-code <CODE>Execute inline BoxLang code, no file needed

Environment variable interpolation

Any BoxLang config file supports ${env.VARIABLE_NAME:defaultValue} placeholders — the default is used when the environment variable is unset, so secrets and per-environment values never need to be hardcoded:

json{
  "datasources": {
    "main": {
      "driver": "mysql",
      "host": "${env.MYSQL_HOST:localhost}",
      "port": "${env.MYSQL_PORT:3306}",
      "database": "${env.MYSQL_DATABASE:myapp}",
      "username": "${env.MYSQL_USERNAME:root}",
      "password": "${env.MYSQL_PASSWORD}"
    }
  }
}

Other built-in placeholders available in any BoxLang config file: ${boxlang-home}, ${user-home}, ${user-dir}, and ${java-temp}.

.env files

Unlike boxlang.json, a .env file in the current working directory is loaded automatically — no flag needed. This was confirmed directly: a script run from a directory containing a .env with MY_VAR=value saw it via System.getenv("MY_VAR"); the same script run one directory over, with no .env present, didn't.

plain — .envMYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_DATABASE=myapp
MYSQL_USERNAME=root
MYSQL_PASSWORD=super-secret

This is what the ${env.VARIABLE_NAME:defaultValue} placeholders above actually resolve against day to day — a .env file per environment (gitignored, never committed) is the natural way to supply real values locally without exporting shell variables or hardcoding secrets into boxlang.json itself.

A real OS environment variable of the same name always wins over the .env file's value — confirmed the same way, by setting MY_VAR in the shell before running a script whose .env set a different value for it, and seeing the shell's value win. .env only fills in what isn't already set, same as the standard dotenv convention everywhere else — safe to layer under CI/production environments that already export real values.

Adding other modules

BoxLang Express is itself just a BoxLang module — the same module system installs a broader ecosystem of modules beyond it. The datasource example above is a good example of where this comes up: things like additional JDBC driver support, security/encoding helpers, or a CFML compatibility layer all ship as separate, opt-in modules rather than being baked into the runtime.

The standard tool for this is CommandBox (the box CLI) — it talks to ForgeBox, the BoxLang/CFML package registry:

bashbox install bx-mysql

A few modules you'll commonly see alongside a BoxLang project:

ModuleWhat it's for
bx-mysqlMySQL JDBC driver support
bx-esapiOWASP ESAPI-backed security/encoding utilities
bx-compat-cfmlAdobe/Lucee CFML compatibility layer

Unlike a typical CommandBox package, a BoxLang module installs into ~/.boxlang/modules/global, machine-wide — by default, since that's where the BoxLang runtime itself looks. Pass --local to install it at the project level instead:

bashbox install bx-mysql --local

A local install lands in boxlang_modules/, and if there's no box.json yet, box install creates one and records the dependency — so a teammate (or CI) cloning the project can run box install with no arguments to fetch everything it depends on.

App-level settings

Separate from the CLI/runtime config above, BoxLang Express has its own small settings bag on the app object, set with app.set(name, value) and read back with app.getSetting(name):

SettingEffect
"views"Directory res.render() resolves view files from. See Views & Templates.
"view engine"Default extension appended to a view name with no extension (defaults to "bxm").
"env"When set to "development", the default 500 handler exposes the real error message instead of a generic one. Custom error middleware can read this the same way — see Error Handling.
"trust proxy"When true, req.ip prefers the first address in X-Forwarded-For over the direct TCP peer. Off by default — an untrusted client could otherwise forge that header to spoof its IP.
"reloadOnChange"Dev-mode auto-restart on file change. See Process Lifecycle.
bxsapp.set( "env", "development" )
app.set( "trust proxy", true )
app.set( "views", expandPath( "./views" ) )