Skip to content

Environment Variables

Compose files use environment variables for configuration. dockmesh extends this with global variables injected into every stack, plus host-scoped variables that only apply to stacks on specific hosts.

When a stack deploys, dockmesh resolves variables in this order (highest wins):

  1. Stack .env file (in the stack directory on disk)
  2. Organization globals from the Environment page (apply to every stack)
  3. Compose defaults (if the Compose file has ${VAR:-default})

This lets you set TZ=Europe/Berlin once as a global and have it injected into every container, while still overriding it per-stack via the stack’s own .env.

Host-tag-scoped globals (“only on hosts tagged prod”) are not implemented today — globals are organization-wide. The same goes for stack-level UI-managed variables: today the per-stack source is the .env file on disk.

Environment in the sidebar manages organization-wide variables, grouped by a free-form group label (purely a UI organisation aid — groups do not change precedence or scope).

Common globals:

VariableExampleWhy
TZEurope/BerlinConsistent timezone across containers
PUID / PGID1000 / 1000LinuxServer.io convention for file ownership
SMTP_HOSTmail.example.comShared email server across all apps
LOKI_URLhttp://loki:3100Shared log shipping
SENTRY_DSNhttps://...Shared error tracking

The global_env schema has an encrypted boolean flag, but the UI doesn’t expose toggling it on import / edit yet — secrets land in the database as plaintext for now. The import preview heuristically flags keys matching password/secret/key/token as worth-marking-secret so you can see what would be encrypted once the backend slice lands. Until then, two pragmatic workarounds:

  • Keep production secrets out of the dockmesh database entirely — use Docker’s native secrets or an external store (Vault, SOPS, age-encrypted files mounted in) and reference them in the compose file.
  • Set the encrypted flag directly via the REST API (PUT /api/v1/global-env/<id>) if you really need at-rest encryption today.

Tracked in the Slice-2 backend punch-list.

Access to env vars is gated on the system.update permission. There is no dedicated env.read_secrets role today — anyone with system.update sees the full value.

Environment → Import accepts a standard .env file:

TZ=Europe/Berlin
PUID=1000
SMTP_HOST=mail.example.com
DATABASE_PASSWORD=s3cr3t

Lines starting with # and blank lines are skipped. The preview shows the parsed keys, suggested-secret flags, and which keys would conflict with existing entries (those get skipped on apply).

A built-in Export to .env flow isn’t shipped — copy the values out via GET /api/v1/global-env if you need them for backup or migration.

Reference globals like any env var:

services:
app:
image: nginx
environment:
- TZ
- PUID
- DATABASE_URL=postgres://user:${DATABASE_PASSWORD}@db/app

Short form (- TZ) imports the variable by name from the environment. Long form with = uses interpolation.

Every change to a global variable is logged — who changed what, when, from where. Use this to trace “who changed the SMTP password at 2am”.

  • Stack Management — where stack-level .env is edited
  • Audit Log — env var change history
  • RBACsystem.update covers global env management today