Skip to content

Outbound Email (SMTP)

TruePPM sends outbound email — @mention notifications and the own-task notifications (a task assigned to you, the planned date of your task changing, a comment on your task) — through Django’s email backend. Delivery is best-effort and opt-in: a notification is emailed only when the recipient has turned the Email channel on for that event under User → Settings → Notifications. Email is off by default.

There is no in-app SMTP credential editor in the community edition. Transport is configured through Django’s standard EMAIL_* settings, and the read-only Workspace → Settings → Email & SMTP page reflects whatever the deployment is configured with.

TruePPM uses Django’s standard EMAIL_* settings. The Beat-driven drain runs on the api, celery, and celery-beat workloads, so all three need the same transport configuration.

SettingPurpose
EMAIL_HOSTSMTP relay hostname. Unconfigured ⇒ “Not configured” on the status page.
EMAIL_PORTSMTP port (e.g. 587).
EMAIL_USE_TLSUse STARTTLS.
EMAIL_USE_SSLUse implicit SSL/TLS (mutually exclusive with TLS).
EMAIL_HOST_USERSMTP username. Never exposed by the API.
EMAIL_HOST_PASSWORDSMTP password. Never exposed by the API, never logged.
DEFAULT_FROM_EMAILFrom address on every message (e.g. notify@example.com).
EMAIL_BACKENDDjango backend; use the SMTP backend in production.

Source EMAIL_HOST_PASSWORD from a secret manager that your settings override reads — never commit it in plain text.

DEFAULT_FROM_EMAIL is the domain receiving mail servers check SPF, DKIM, and DMARC alignment against. TruePPM sends the mail; your DNS configuration is what makes it trusted:

  • Use a DEFAULT_FROM_EMAIL domain you control and have published SPF and DKIM DNS records for. Most self-hosters send through a relay (Amazon SES, SendGrid, Postmark, or their own mail server via EMAIL_HOST) — the relay’s setup docs walk through adding the required SPF (TXT) and DKIM (CNAME/TXT) records at your registrar or DNS host.
  • DMARC alignment requires the From domain (DEFAULT_FROM_EMAIL) to match either the SPF-authenticated domain or the DKIM-signing domain. If your relay signs with a different domain than DEFAULT_FROM_EMAIL, alignment fails even though the message was accepted and delivered by the relay.
  • A relay reporting “sent successfully” only confirms SMTP accepted the message — it says nothing about SPF/DKIM/DMARC alignment at the receiving end. Misaligned records are the most common reason self-hosted notification email lands in spam even though delivery looked fine from the sending side.

TruePPM has no setting that can enforce or verify this for you — SPF, DKIM, and DMARC are DNS records you own and publish, not something a .env value or Helm value configures.

Workspace → Settings → Email & SMTP (workspace Admins and Owners only) shows the resolved transport mode, host, port, TLS/SSL, and From address. It never displays the username or password and cannot change configuration — update the Django settings and redeploy to change transport. A writable in-app SMTP configuration surface is a planned follow-up, not part of the community edition today.

  • Email is queued as a notification row and sent by the drain_notification_emails Beat task (every 30 s), never inline — a broker or SMTP outage delays delivery but does not block the triggering action.
  • Each message is retried up to 3 times; after that the notification remains in the in-app inbox but stops attempting email.
  • Bodies are plain text. A recipient with no email address is skipped (the in-app notification still appears).
  • Bodies carry a direct deep-link to the affected task when FRONTEND_BASE_URL is set (e.g. the task.blocked email links straight to the blocked task). Leave it empty and the email still renders — it just omits the link.
  • Comment/mention snippets embedded in the body are bounded and word-wrapped before sending, so a very long unbroken string (a pasted URL, log line, or base64 blob) can’t render as one unbounded line in the recipient’s mail client.

Notification email carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers (RFC 8058), pointed at the recipient’s User → Settings → Notifications page. Gmail, Outlook, and other large mailbox providers weight the presence of these headers into their bulk-sender spam heuristics, so including them helps delivery even at TruePPM’s low, opt-in notification volume.

The headers link to the login-gated preferences page, not a no-auth one-click unsubscribe endpoint — TruePPM issues no per-notification unsubscribe token, so “one click” here means one click through to sign-in and preferences, not an anonymous unsubscribe. The headers are only added when FRONTEND_BASE_URL is configured, since a bare relative path is not a valid header value; leave it unset and the email still sends, just without them.

Leave SMTP unconfigured to run without outbound email — in-app notifications keep working and the status page reports the transport as not configured.