Meridian handbook
Reference

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

VariableRead byEffect
NO_COLOREvery commandWhen non-empty, no colour in terminal output (as --no-colour)
TERMEvery commanddumb 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.

VariableInterpolated atStarter defaultNotes
ENVIRONMENTengine.environmentdevelopmentdevelopment, staging or production
LISTEN_ADDRserver.listen127.0.0.1:8080Set 0.0.0.0:8080 in a container
BASE_URLserver.base_urlhttp://localhost:8080The default site’s public origin; required in production
DATABASE_URLdatabase.urlNone: must be setA secret
LOG_FORMATlogging.formattextjson in production

Names the deployment pages and examples add:

VariableInterpolated atNotes
SIGNING_KEYserver.security.signing_keyA secret, at least 32 characters. Required in production
MEDIA_SIGNING_KEYmedia.signing_keyA secret; optional
ADMIN_HOSTserver.admin_hostA host of the admin’s own, or * for every site’s host
MIGRATE_ON_BOOTmigrate.on_bootrefuse or apply
LOG_LEVELlogging.leveldebug, info, warn or error
SEARCH_DRIVERsearch.drivermeilisearch, elasticsearch, opensearch or none
MEILISEARCH_URLsearch.meilisearch.urlSuch as http://search:7700
MEILISEARCH_API_KEYsearch.meilisearch.api_keyA secret; set it empty for a server with no key
MEILISEARCH_INDEX_PREFIXsearch.meilisearch.index_prefixWhen several installs share one Meilisearch
METRICS_BINDtelemetry.metrics.bindSuch as 0.0.0.0:9090 on a private network
PDF_RENDERERanalytics.pdf.rendererauto, browser or builtin
SMTP_HOST, SMTP_USERNAME, SMTP_PASSWORDmail.smtp.*The password is a secret
S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYstorage.s3.*The secret access key is a secret
SENTRY_DSNtelemetry.errors.dsnA secret
AI_ENABLEDai.enabledtrue or false; ai.enabled is a string so it can be interpolated
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRETstripe.secret_key, stripe.webhook_secret in config/paidlistings.yamlengine-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:

CommandFlagDefault variable
engine-dmo mcp call--token-envMERIDIAN_TOKEN: a personal access token for the remote content MCP
engine-dmo site export--signing-key-envNone: the receiving install’s signing key
engine-dmo affiliates connect--secret-envNone: a network’s reporting API secret; standard input when not given

Variables the engine sets for child processes

VariableSet forValue
MERIDIAN_ENGINEThe frontend build engine-dmo build runs, and the Vite development server serve --dev starts through the Vite extensionThe path of the running engine binary, which the Vite plugin calls for the asset manifest
MERIDIAN_PROJECTThe sameThe absolute project directory
MERIDIAN_VITE_ORIGINThe Vite development serverThe origin Vite should serve on
MERIDIAN_PLUGIN_*A plugin process declared in plugins.yamlThe 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.