SMTP Configuration
1. Overview
Outbound email is sent by admin-service (registration confirmations, membership cards, password resets, receipts) and occasionally by portals (logout confirmations, system alerts). All services use Spring’s JavaMailSender via the stock spring-boot-starter-mail. Configuration is environment-specific — prod uses Microsoft 365 / Exchange Online via myriadevents-co-za.mail.protection.outlook.com; dev uses GreenMail.
This page documents the config surface, the production SMTP provider, and the dev fallback.
See also: Docker Compose for Developers, GreenMail Email Server.
2. Configuration Surface
Every service exposes the same spring.mail. tree. Values flow from Helm → ConfigMap + Secret → env vars → application-.yml.
spring:
mail:
host: ${MAIL_HOST}
port: ${MAIL_PORT:25}
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
protocol: smtp
properties:
mail:
smtp:
auth: true
starttls:
enable: true
ssl:
trust: ${MAIL_HOST}
test-connection: false
Service-specific extras:
application:
mail:
from: ${MAIL_FROM:noreply@localhost}
base-url: https://${MAIL_BASE_URL} # used in email templates for absolute links
application.mail.from is the From: header on every outbound message; must be a deliverable address at the provider (Exchange rejects senders not owned by the tenant).
3. Production: Microsoft 365 / Exchange Online
Provider: myriadevents-co-za.mail.protection.outlook.com:25 with STARTTLS and basic auth.
From ArgoCD event-admin-service-prod.yml:
mail:
host: myriadevents-co-za.mail.protection.outlook.com
port: 25
username: [email protected]
# password: via existingsecret.mailpassword
protocol: smtp
tls: true
properties.mail.smtp:
auth: true
ssl.trust: myriadevents-co-za.mail.protection.outlook.com
starttls.enable: true
Notes:
-
Port 25 — standard SMTP; the Exchange connector accepts STARTTLS on 25. Port 587 (submission) also works if 25 is blocked by network egress policy.
-
From address —
[email protected]. Must match the authenticated identity or Exchange rejects with5.7.1 Client does not have permissions to send as this sender. -
SPF / DKIM — configured at the
myriadevents.co.zaDNS level. Any outbound email not coming through this connector will fail SPF at the recipient; keep that in mind if a future integration tries to send directly via a third-party mail provider. -
Rate limits — Exchange Online has per-connector and per-sender throttles (typically hundreds per hour for regular accounts, thousands for connector-style). Large batch emails (membership card annual mail-out) must be throttled on the application side; the service’s
CommunicationLogtracks sends and is the enforcement point.
3.1. Per-tenant From addresses
Multi-tenant portals may want to send as [email protected] for each tenant. Two approaches:
-
Single shared connector, rewrite From at the application layer. Exchange permits you to send as
[email protected]only if themyriadevents.co.zatenant has been delegated that domain (unlikely for most tenants). -
Per-tenant SMTP config. Each Organisation has its own
mail.host/port/username/password/from. Service picks the right config per sending context. Scales better for genuine multi-tenancy but adds config complexity.
Currently all tenants share the myriadevents.co.za sender; scale to per-tenant only when a tenant specifically asks for branded From addresses. Capture the pattern in a design-journal thread when it becomes concrete.
4. Dev: GreenMail
Provider: local GreenMail container (image: greenmail/standalone:2.1.0).
# admin-service/src/main/resources/config/application-dev.yml (relevant part)
spring:
mail:
host: localhost
port: 3025
properties:
mail:
smtp:
auth: false # GreenMail accepts anything
starttls:
enable: false
See GreenMail Email Server for the full dev setup.
Mail browsing:
-
Web UI: http://localhost:8080 — shows inboxes per address
-
IMAP: port 3143 (automated test consumption)
-
Username/password:
[email protected]/dev(configurable viaGREENMAIL_OPTS)
4.1. dev-to-stage SMTP for bigger testing
If dev needs realistic mail flow (e.g. testing that a customer actually receives a registration confirmation), switch application-dev.yml to the Exchange connector — but point application.mail.from at a test address and be careful about volume. Typically this is only needed briefly; revert after.
5. Stage
Stage uses the same Exchange connector as prod. Reason: we explicitly want stage to send real mail during UAT, to a small known list of test addresses ([email protected], etc.). Do not point stage at dev GreenMail — it silences the very thing we’re testing.
ArgoCD event-admin-service-stage.yml mirrors prod mail config; the only deltas are database URL and image version.
6. Template Rendering
Templates live in admin-service as Thymeleaf files under src/main/resources/templates/mails/. Variables resolved against the service’s MailService; email is assembled and sent synchronously.
Advanced: the "email implementation layer" design (see design-journal/2026-03/email-implementation.adoc) introduces DB-backed org-scoped templates via MJML + Thymeleaf. When that lands, the mail provider configuration stays identical; only the template-rendering layer changes.
7. Observability
Sent email is logged at INFO:
INFO ... MailService : Email sent to '[email protected]' with subject 'Registration confirmation' in 187ms
Failures log at ERROR with the full exception chain. For production debugging, CommunicationLog records every attempt with delivery status.
No email body or PII is logged. Subject is considered low-sensitivity and is logged.
8. Security
-
Credentials — always via Kubernetes Secret. Never in
values.yaml, never in git, never inapplication-prod.yml. -
TLS — mandatory via
starttls.enable: truefor any non-dev environment. Exchange rejects plaintext after the TLS handshake requirement is active. -
Rate-limit — application-level throttling for bulk sends; Exchange enforces its own throttle and will
4.7.0responses if exceeded. Handle the retryable SMTP error inMailService.
9. New Service Onboarding
When admin-portal eventually needs to send email (unlikely at launch — admin-portal typically triggers emails via admin-service, not directly):
-
Copy
spring.mail.*tree from registration-portal’sapplication-prod.yml. -
Add
mailsection tovalues.yamlwith the same structure. -
Point at
event-admin-portalSecret formailpassword. -
No new Exchange connector config needed — reuse the tenant-level connector.
Preferred: do not add mail to admin-portal. Route all outbound email through admin-service where CommunicationLog captures the send. Portals should request emails (via admin-service API) rather than send directly.
10. Reference
| File | Role |
|---|---|
|
Base |
|
Dev override pointing at GreenMail |
|
Thymeleaf email templates |
|
Production Exchange connector config |
|
Service-level sender |