Vaultwarden treats defined-but-empty SMTP_HOST/SMTP_FROM as "set"; with upstream _enable_smtp defaulting true its validation errors out and the container crash-loops on every fresh deploy with app-email off (the default). Gate the SMTP group with _ENABLE_SMTP: compose default false (clean boot, mail off), flipped "true" by the app-email injection via smtp_mapping.extra. Proven on the pinned 1.33.2-alpine image (P1 repro exit 12 / P2 off boots / P3 on boots). REUSE.md: strict-image TRAP row. Sweep note: the other 5 smtp-mapped templates are campaign-boot-proven tolerant; no edits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
3.0 KiB
REPORT — vaultwarden F1 fix: _ENABLE_SMTP boot-gate (2026-07-06)
Class: implementation (campaign-finding fix; push = deploy).
Baseline: main @ 13eedb1 → (this commit). Provenance: felhom.eu
documentation/audits/CAMPAIGN-nomercy-2026-07-06.md finding F1 (pilot-blocking: every fresh
vaultwarden deploy with app-email off — the default — crash-loops).
Corrected root cause (the campaign report's hypothesis was wrong)
Not "shim sets SMTP_FROM without SMTP_HOST" — with app-email off the shim injects nothing. The
crasher: the template always defines SMTP_HOST=${SMTP_HOST:-} and SMTP_FROM=${SMTP_FROM:-};
vaultwarden's get_env_str_value returns Some("") for a defined-empty var (empty counts as
set), and with _enable_smtp defaulting true upstream, its validation
smtp_host.is_some() == smtp_from.is_empty() → true == true → config error → exit → crash-loop.
The old template comment ("empty SMTP_HOST = mail stays disabled") was wrong for this image.
Empirical probe (mandatory gate, run on 180 against the pinned image BEFORE editing)
| Probe | Env | Result |
|---|---|---|
| P1 (pre-fix shape) | SMTP_HOST= SMTP_FROM= (defined-empty) |
exit 12 — "Both SMTP_HOST and SMTP_FROM need to be set for email support without USE_SENDMAIL" (exact campaign error) — the image-level red-proof |
| P2 (fix, OFF) | + _ENABLE_SMTP=false |
boots — "Rocket has launched", running |
| P3 (fix, ON) | _ENABLE_SMTP=true + host + from |
boots — running (no SMTP connect at boot) |
(P2/P3 needed I_REALLY_WANT_VOLATILE_STORAGE=true to bypass the unrelated no-volume guard in the
bare docker run; P1 control re-run with it still crashes on the SMTP error, isolating the cause.)
Logs: DooPlex ~/campaign-nomercy-2026-07-05/evidence/PROBE-F1/.
Changes
templates/vaultwarden/docker-compose.yml—_ENABLE_SMTP=${_ENABLE_SMTP:-false}added to the SMTP group; the wrong comment replaced with the empty-counts-as-set TRAP note (+ config.json admin-panel precedence note). Nothing else touched.templates/vaultwarden/.felhom.yml—smtp_mapping.extra: _ENABLE_SMTP: "true"(rides the existingsmtpEnvextra loop; zero controller change).REUSE.md— TRAP appended to the App-email row: strict images need the enable-flag pattern; boot-prove fresh email-off deploys for every new smtp-mapped app.CHANGELOG.md— entry incl. the sweep note.
Sweep note (documentation only, no edits)
calcom, gitea, mealie, nextcloud, rallly (and gitea's GITEA__mailer__SMTP_ADDR=${...:-}) are
boot-proven tolerant of defined-empty mail env — all ran healthy as fresh email-off deploys in
the campaign. Vaultwarden was the only strict image.
Validation
- Probe gate P1/P2/P3 above (image level).
- Live pipeline validation (Scenario A fresh email-off deploy healthy; Scenario B toggle ON→OFF regression) executed as part of the controller v0.101.0 deployment — see felhom-controller/REPORT.md for the live evidence.