Adding Environment Variables
How to add a new variable to config/.env and have it propagate through the compose files into a running service.
config/.env is the single source of truth, but a variable only reaches a
container if a compose file references it. The stack deliberately does not dump
the whole .env into every service — each service lists exactly the variables it
needs as KEY: ${KEY:-default}. Adding your own value is a two-line change plus one
apply.
When you need this
Reach for this when a feature (a new integration, a provider SDK, a feature flag)
expects an environment variable that isn't already wired in. If the variable is
already listed in a service's environment: block, you only need to set it in
.env — skip straight to Step 3.
Step 1. Declare it in config/.env
Add the variable (and, ideally, a labelled comment) to config/.env:
Grouping it under a clear section keeps .env readable. For secrets you'd rather
have generated for you, leave the value blank so intelliask secrets gen can
fill it — see Secrets.
Step 2. Reference it in the compose file
Open the compose file for the service that needs the value (e.g.
config/intelliask.compose.yml) and add the variable to that service's
environment: map, using the same ${VAR:-fallback} interpolation pattern as the
rest of the stack:
${MY_FEATURE_TOKEN:-}injects the value from.env, or an empty string when it's unset — this is why the app never crashes on a missing optional variable.- Use
${MY_FEATURE_TOKEN:-somedefault}to bake in a default when the var is blank. - The name on the left is what the container sees; the
${...}on the right is what's read from.env. They're usually identical, but don't have to be.
Editing .env alone is not enough
Because services only receive the variables their compose file lists, adding a key
to .env without a matching environment: line means the container never sees
it. Always do Step 1 and Step 2.
Adding a brand-new compose file
If your customisation is large enough to warrant its own service or override, create
a custom.compose.yml and register it so docker compose loads it alongside the
rest of the stack — either append it to COMPOSE_FILE in config/.env:
…or drop a docker-compose.override.yml next to the compose files (Compose merges
it automatically). See
Alternative Deployment
for the trade-offs.
Step 3. Apply
Recreate the affected service so it picks up the new environment. Compose only
re-reads .env and re-injects variables when the container is (re)created, so a
plain restart is not enough:
Confirm the value landed inside the container:
Changing a value later
Editing an already-wired variable follows the same rule: update it in .env, then
intelliask up <service> to recreate the container. intelliask restart reuses the
existing container and will not pick up the change.
Worked example: connecting IntelliApps
IntelliApps is a good example of wiring a value into the IntelliAsk container. IntelliAsk reaches it as a native MCP server that is injected automatically at container build only when two variables are present:
| Variable | Purpose |
|---|---|
INTELLIAPPS_API_URL | Base URL of the IntelliApps service — the injected MCP server points at ${INTELLIAPPS_API_URL}/mcp. |
INTELLIAPPS_SERVICE_KEY | Shared service key sent as the X-App-Service-Key header. Must equal the IntelliApps service's own HTML_APP_MCP_API_KEY. |
Declare them in config/.env:
Reference them on the intelliask service:
Then intelliask up intelliask. On start the app logs
[IntelliApps] Native MCP server "intelliapps" injected… when both are set, or
… NOT injected — missing env: … when one is absent.
Legacy names
The older HTML_APPS_API_URL / HTML_APPS_SERVICE_KEY names are still accepted as
fallbacks. For the full IntelliApps environment surface, see
Services → IntelliApps → Configuration.
Last updated on