Environment variables
Which environment variables the engine reads directly (very few), the conventional names a project's engine.yaml interpolates, the variables commands read secrets from, and the variables the engine sets for child processes.
The engine reads almost nothing from the environment on its own. Configuration reaches it through the ${VAR} interpolations in your configuration files, so the variable names are whatever your engine.yaml says. A new project’s files, the examples and this handbook use the conventional names below; keep them unless you have a reason not to.
Read directly by the engine
| Variable | Read by | Effect |
|---|---|---|
NO_COLOR | Every command | When non-empty, no colour in terminal output (as --no-colour) |
TERM | Every command | dumb turns colour off |
Nothing else changes the engine’s behaviour unless your configuration names it. The engine does not read .env files.
Conventional names in engine.yaml
These are the names engine-dmo init writes into a new project’s config/engine.yaml, and the ones the deployment pages use. Each is only read because the file interpolates it.
| Variable | Interpolated at | Starter default | Notes |
|---|---|---|---|
ENVIRONMENT | engine.environment | development | development, staging or production |
LISTEN_ADDR | server.listen | 127.0.0.1:8080 | Set 0.0.0.0:8080 in a container |
BASE_URL | server.base_url | http://localhost:8080 | The default site’s public origin; required in production |
DATABASE_URL | database.url | None: must be set | A secret |
LOG_FORMAT | logging.format | text | json in production |
Names the deployment pages and examples add:
| Variable | Interpolated at | Notes |
|---|---|---|
SIGNING_KEY | server.security.signing_key | A secret, at least 32 characters. Required in production |
MEDIA_SIGNING_KEY | media.signing_key | A secret; optional |
ADMIN_HOST | server.admin_host | A host of the admin’s own, or * for every site’s host |
MIGRATE_ON_BOOT | migrate.on_boot | refuse or apply |
LOG_LEVEL | logging.level | debug, info, warn or error |
SEARCH_DRIVER | search.driver | meilisearch, elasticsearch, opensearch or none |
MEILISEARCH_URL | search.meilisearch.url | Such as http://search:7700 |
MEILISEARCH_API_KEY | search.meilisearch.api_key | A secret; set it empty for a server with no key |
MEILISEARCH_INDEX_PREFIX | search.meilisearch.index_prefix | When several installs share one Meilisearch |
METRICS_BIND | telemetry.metrics.bind | Such as 0.0.0.0:9090 on a private network |
PDF_RENDERER | analytics.pdf.renderer | auto, browser or builtin |
SMTP_HOST, SMTP_USERNAME, SMTP_PASSWORD | mail.smtp.* | The password is a secret |
S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | storage.s3.* | The secret access key is a secret |
SENTRY_DSN | telemetry.errors.dsn | A secret |
AI_ENABLED | ai.enabled | true or false; ai.enabled is a string so it can be interpolated |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | stripe.secret_key, stripe.webhook_secret in config/paidlistings.yaml | engine-dmo only. Secrets |
Setting them in development
Once a file names a variable with no default, every command that loads the configuration needs it set, on your laptop as much as in production. Which commands load what is in Commands and the environment. For local work without a database, and once engine.yaml names ${SIGNING_KEY} for production:
export DATABASE_URL=postgres://localhost/placeholder # any address; check --no-db and describe never connect
export SIGNING_KEY="" # empty is a derived development key; or 32 or more random characters SIGNING_KEY set to 1 to 31 characters is refused everywhere (signing_key must be at least 32 characters). With a real development database, set DATABASE_URL to it. The engine does not read .env files: export the variables in your shell, or load a file yourself (set -a; . ./.env; set +a).
Only string-typed settings can be interpolated; a boolean or number setting cannot come from a variable. Configuration and secrets explains ${VAR}, ${VAR:-default} and the secret rule.
Variables commands read secrets from
Some commands take a secret from a variable you name, so it never appears on the command line or in shell history:
| Command | Flag | Default variable |
|---|---|---|
engine-dmo mcp call | --token-env | MERIDIAN_TOKEN: a personal access token for the remote content MCP |
engine-dmo site export | --signing-key-env | None: the receiving install’s signing key |
engine-dmo affiliates connect | --secret-env | None: a network’s reporting API secret; standard input when not given |
Variables the engine sets for child processes
| Variable | Set for | Value |
|---|---|---|
MERIDIAN_ENGINE | The frontend build engine-dmo build runs, and the Vite development server serve --dev starts through the Vite extension | The path of the running engine binary, which the Vite plugin calls for the asset manifest |
MERIDIAN_PROJECT | The same | The absolute project directory |
MERIDIAN_VITE_ORIGIN | The Vite development server | The origin Vite should serve on |
MERIDIAN_PLUGIN_* | A plugin process declared in plugins.yaml | The socket, protocol version, plugin name and lifeline. A plugin’s env may not declare these names |
Set MERIDIAN_ENGINE yourself in a container build stage that runs the Vite build, so the plugin uses the engine binary you copied in rather than looking for one (see Container images).
A plugin process receives only PATH, HOME, TMPDIR, USER, LANG, LC_ALL, SSL_CERT_FILE and SSL_CERT_DIR from the engine’s environment, plus what its env declares and TZ=UTC. It never sees the database URL or a key it was not given.
Template API
How to get the complete, generated template surface for your own project (every global, filter, tag, field type, your types and fields, routes and components) from engine-dmo template api, docs serve, describe and guide.
Exit codes
The five exit codes every engine-dmo and engine-cms command uses (0 success, 1 error, 2 pending or unconfirmed, 3 invalid configuration, 4 degraded), and which commands return 2 and 4 and why.