@Mail Domain Check

Docker Compose / SMTP / password reset

SMTP Is in .env, but Docker Compose Password Reset Email Is Not Sent

A project-level .env file can feed Compose interpolation without placing those variables inside the application container. Prove the resolved service, running process, controlled send, provider event, and link page as five separate boundaries.

Generate the next action

Browser-local boundary planner

Turn three observed states into one next action

No .env value, SMTP password, recipient, reset URL, token, message, provider ID or credential is requested or transmitted.

The five boundaries

1. Interpolation
Compose can read values from a project .env file while resolving ${VARIABLE} expressions.
2. Service export
Only variables declared through the service environment or env_file configuration are placed in that container.
3. Running process
The created app container must actually contain the key names expected by the deployed application revision.
4. Send path
The password-reset handler must call the mailer and receive either a bounded failure or provider acceptance.
5. Completion
Receiver visibility and successful reset-link consumption remain separate from SMTP configuration.

Do not paste .env output, SMTP values, a recipient address, reset URL, token, provider message identifier, private key, seed phrase or wallet signature into a public issue or diagnostic.

What the source proves in Riffado

At Riffado revision 731c728314a685a488d0f03ab21828f244f60028, .env.example declares optional SMTP_* variables and the mail code reads those names from process.env. The official docker-compose.yml app service enumerates its environment but does not include the SMTP keys. That is enough to test the service-export boundary before changing DNS, the receiver mailbox, or the reset page.

The same issue also reports a reset-page failure. Keep that as a later client/server and token-consumption investigation: a missing email and a crashing link page are two independent defects.

This is source evidence for that revision, not a claim about every deployment. A local override file, another Compose file, a manually supplied environment, or a later revision can change the effective model.

Prove key presence without printing values

Run each check from the directory and Compose-file set used for the actual deployment. The first command lists interpolation key names only; the second lists key names exported by the resolved app service.

docker compose config --environment | cut -d= -f1 | sort

docker compose config --format json \
  | jq -r '.services.app.environment | keys[]' \
  | sort

Then check boolean presence inside the running application process. This command does not print any value:

docker compose exec app node -e \
"console.log(Object.fromEntries(
  ['SMTP_HOST','SMTP_PORT','SMTP_SECURE','SMTP_USER','SMTP_PASSWORD','SMTP_FROM']
    .map(k => [k, Boolean(process.env[k])])
))"

docker compose config --environment without the key-only filter can expose interpolation values. Keep raw output private and never attach it to a public report.

Pass the variables into the app service

When the resolved service omits the keys, add explicit mappings to the existing app.environment block. Keep the actual values in a protected, untracked environment source:

services:
  app:
    environment:
      SMTP_HOST: ${SMTP_HOST:?set SMTP_HOST}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_SECURE: ${SMTP_SECURE:-false}
      SMTP_USER: ${SMTP_USER:?set SMTP_USER}
      SMTP_PASSWORD: ${SMTP_PASSWORD:?set SMTP_PASSWORD}
      SMTP_FROM: ${SMTP_FROM:?set SMTP_FROM}
      SMTP_MARKETING_FROM: ${SMTP_MARKETING_FROM:-}
      SMTP_REPLY_TO: ${SMTP_REPLY_TO:-}
      EMAIL_SEND_RATE_PER_SECOND: ${EMAIL_SEND_RATE_PER_SECOND:-1}

An env_file can also populate a container, but an explicit list limits accidental export. Docker recommends secrets rather than environment variables for sensitive values; adopting Compose secrets also requires the application to read file-backed values. Do not assume that support exists without changing and testing the application.

Recreate, then send once

docker compose up -d --force-recreate app

A plain docker compose restart does not apply changed environment configuration. After recreation, repeat the presence-only check, trigger one password reset for a controlled account, and correlate its UTC timestamp to one application result and one provider event.

  1. If the app still reports SMTP not configured, verify non-empty required values and the exact names read by the deployed revision.
  2. If the transport fails, retain only the bounded status or exception and fix that named boundary.
  3. If the provider accepts the message, follow that exact event through delivery, delay, bounce, suppression, or receiver acceptance.
  4. If the message is visible, test the newest reset link once as a separate route and token-use proof.
Need reusable incident, change, rollback, and weekly-review worksheets?

Read the three-section sample first. The complete 13-section Web3 Email Authentication Operations Kit costs exactly 1 native USDC on Base only when it is useful to you.

Preview free sample

The production-ready proof

Call the mail path ready only when the resolved service includes the expected keys, the current app process confirms presence without revealing values, the application records one reset-send attempt, and the provider or receiver records the same message instance. Record reset-link completion separately.

This independent guide is not affiliated with Riffado or Docker. The effective Compose model, running container, application logs, provider events, and controlled receiver remain the sources of truth for their respective boundaries.

Primary references