Skip to content

Pass secrets to your app

Choose environment variables or mounted files for application secrets.

Choose where a secret is stored with its provider declaration. Separately, choose how your app reads it: as an environment variable or a mounted file. You can change delivery without moving to a different secret store.

Environment delivery

Secret-backed environment bindings use the environment's selected delivery mode along with non-secret values:

yaml
environment-delivery:
  default: direct
  development-default: env-file

environments:
  production:
    environment-delivery: direct
  • direct passes values through the runtime process environment.
  • env-file writes private temporary environment files for the operation.

When a development environment is declared, it defaults to env-file and other environments default to direct. Inspect the actual setting:

sh
mm env delivery show
mm env review
mm env delivery set production direct

Environment variables are visible to the process and may be exposed by application diagnostics or privileged runtime inspection. Use mounted files when the application supports them and narrower exposure matters.

File delivery

Declare file consumers under the service. The map key is the file name under the runtime's secret directory, not a host path:

yaml
schema-version: 1
project:
  id: 019c527b-6242-7b43-bfd4-bd60e27c1de3
  name: secret-files
target: local
stacks:
  app:
    files: [compose.yaml]
modules:
  api:
    path: api
    services:
      api:
        stack: app
        secret-files:
          database-password:
            secret: database.password
            uid: "0"
            gid: "0"
            mode: 292 # decimal form of 0444
secrets:
  database.password:
    storage:
      provider: age
      location: local/database-password
environments:
  local:
    bindings: {}

The deployment plan shows secret names, versions, permissions, and consuming services, without printing values. If a secret changes after review, make a new plan.

Runtime behavior differs:

RuntimeFile deliveryImportant constraint
Podman ComposeVersioned native Podman secretNative store; UID/GID/mode supported
Docker SwarmVersioned immutable Swarm secretAvailable on every node; UID/GID/mode supported
Docker ComposePrivate operation-scoped source fileNo native store; requires UID 0, GID 0, mode 0444

Podman and Swarm receive material on standard input. Rotation creates a new owned revision, updates workloads, and retains the previous revision for rollback. Inspection and cleanup use labels and metadata and do not read secret content.

Docker Compose writes a 0600 source in a private operation directory and removes the path after container creation; the mounted inode remains available to that container. Compose cannot enforce requested file ownership or mode, so other values fail before material is written. Rotation also fails early when a rollback-safe update cannot be guaranteed.

Check metadata without revealing values:

sh
mm secret status
mm env review
mm deploy plan --output json

These views omit credentials and secret values. Host and container-engine administrators can still access secrets used by workloads.

Build secrets

For sensitive build inputs, use modules.<name>.build.secrets to map a BuildKit secret ID to a declared mm secret. Do not use public build arguments for tokens. mm passes build secrets through private files after plan review. See the build configuration reference.

A build secret can change the image, and its value can differ between environments, so mm never reuses or promotes a build that mounts one. When a secret only authenticates fetching inputs, such as a Composer or npm token for private packages, list it under credential-only-secrets:

yaml
modules:
  web:
    build:
      secrets: { composer_auth: composer.auth }
      credential-only-secrets: [composer_auth]

The secret is still mounted, but it is left out of the build fingerprint, so the same inputs reuse the same image whoever builds them and however often the token rotates. Only list a secret here when the image does not depend on its value.