MailerSend decommission plan
Updated August 11, 2026.
This is the authoritative migration checklist for retiring MailerSend without silently breaking church notifications. The 2025 Goodbye, SendGrid page explains why MailerSend was introduced; it is historical context, not evidence that every listed consumer is still active.
Current decision
MailerSend is still an active production dependency. Do not cancel the account, remove DNS records, revoke credentials, or delete protected variables yet. A bounded repository audit found both API consumers and historically documented SMTP consumers. Each path must move independently and pass a real delivery and failure-path test before provider retirement.
No credential values belong in Git, documentation, tickets, job output, or
test email bodies. Repository code may name the protected variables
MAILERSEND_API_KEY, MAILERSEND_FROM_EMAIL, and MAILERSEND_FROM_NAME; the
values remain in the approved runtime boundary.
Repository consumer inventory
| Workflow | Evidence | Migration acceptance |
|---|---|---|
| Sermon-slide pastor and production notifications | Portal scripts/sermon-slide-notification-worker.py and its focused worker tests |
Both distinct recipient emails deliver through the replacement; success, attention, retry, and cost/runtime content remain correct. |
| Support intake and staff notifications | Portal dashboard/v2/support/submit.php, dashboard/v2/support/support.php, and support smoke scripts |
A synthetic ticket reaches the intended staff route; user confirmation and provider-failure behavior are verified without creating duplicate tickets. |
| Key-fob reminders, digests, approvals, and resends | Portal key-fob scripts and dashboard/v2/keys/ handlers |
Reminder, digest, approval, and resend paths deliver once to reviewed recipients and preserve existing audit state. |
| Q-SYS and file-storage monitoring alerts | Portal Q-SYS watch/listen helpers and scripts/files-storage-watch.php |
A controlled warning and recovery notification are received; deduplication and cooldown behavior are preserved. |
| Expense reports | Portal v1.1/v2 expense common code and the retained 2026 form handler | A non-production request proves submitter and reviewer delivery, attachment/link handling, and provider-error visibility. Retire obsolete form code separately rather than assuming it is inactive. |
| Node-RED email flows | Infrastructure nodered/instances/rocc-db-local/flows.json |
Inventory the live flow by stable node identifier, migrate one flow at a time, and verify the real caller plus Node-RED error path. Do not copy flow credentials into the migration ledger. |
The code inventory is a lower bound. Runtime jobs, rollback releases, and external SMTP devices can remain consumers even when the current repository no longer references them.
External SMTP inventory to re-verify
The 2025 documentation identifies these shared-SMTP consumers:
- Bitwarden;
- the Copy Room Fiery scan-to-email workflow;
- Jamf;
- BillionMail and the Node-RED workflows that call it;
- Wi-Fi administrator messages;
- Planning Center weekly Wi-Fi access messages;
- key-fob confirmations.
For each system, record only owner, hostname/service name, sender domain, replacement status, test date, and reviewer. Confirm the live configuration without displaying usernames, passwords, tokens, message contents, or a full environment file. An unverified historical row is not permission to remove it.
Required replacement capabilities
The selected replacement must support:
- the current sender identities and distinct pastor/staff recipient routing;
- authenticated API delivery for application emails and authenticated SMTP for devices that cannot use an API;
- TLS certificate validation and protected, least-privilege credentials;
- bounded retries with idempotency or application-side deduplication;
- bounce, suppression, complaint, and provider-failure visibility;
- SPF, DKIM, and DMARC alignment for every active sender domain;
- content-safe operational logging with timestamps, provider request IDs, latency, result class, and cost when applicable;
- a documented monthly owner and spending/volume limit.
Provider choice is intentionally not made by this inventory. Self-hosting mail introduces deliverability, abuse, reputation, patching, backup, and on-call ownership that must be accepted explicitly; it is not automatically safer or cheaper than a managed provider.
Migration sequence
- Assign an owner and replacement transport for every repository and external row. Mark unknown live consumers as blockers.
- Configure the replacement in a protected development boundary. Never clone production values into development.
- Add provider-neutral application configuration while keeping MailerSend as a sealed rollback path. Do not dual-send normal user mail.
- Exercise each workflow with reviewed test recipients. Verify recipient, sender, reply-to, links/attachments, latency, logs, and a forced failure.
- Move one production workflow at a time. Observe at least one normal operating cycle and confirm no unexplained MailerSend activity remains.
- Search current source, GitLab variable names, OpenBao metadata, systemd/cron definitions, Node-RED node metadata, and device inventory without displaying values. Separately inspect provider usage and bounce/suppression activity.
- Remove MailerSend credentials from active consumers, prove old-auth rejection, and retain a time-bounded rollback record only when policy requires it.
- Update SPF/DKIM/DMARC deliberately and verify alignment before removing old provider DNS records.
- Cancel the MailerSend subscription/account only after an owner approves the evidence ledger and the observation window has passed.
Completion evidence
MailerSend is decommissioned only when all of the following are true:
- [ ] Every repository row has a replacement, test owner, production cutover, and failure-path result.
- [ ] Every historically documented SMTP consumer is verified migrated or explicitly retired.
- [ ] Current source and runtime metadata contain no active MailerSend consumer.
- [ ] Provider activity remains at zero for the approved observation window.
- [ ] Replacement SPF, DKIM, DMARC, bounce, suppression, alerting, and cost ownership are verified.
- [ ] Old credentials are rejected and removed from active GitLab/OpenBao/host boundaries without exposing their values.
- [ ] Rollback and incident runbooks name the replacement transport.
- [ ] The account owner approves provider cancellation and records the date.
Until every box is complete, the correct task status is in progress, not decommissioned.