Skip to content

MailerSend decommission plan

Updated August 22, 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 must remain available for rollback and unverified external SMTP consumers. Do not cancel the account, remove DNS records, revoke credentials, or delete protected variables yet. Current Portal application mail and the primary rocc-db-local Node-RED mail flows use the Cloudflare gateway, but the Fiery, Bitwarden, Jamf, and BillionMail paths have not all passed live replacement tests. Each external 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.

Migration status snapshot

This snapshot records evidence gathered on August 22, 2026. It distinguishes current default-branch code from historical documentation and old feature branches.

Consumer or control Status Evidence / remaining gate
Cloudflare email gateway Production foundation active Terraform/OpenTofu manages the Worker, Queue, D1 metadata outbox, sender/client identities, 400-delivery rolling daily cap, 2,300-delivery rolling 30-day cap, and account budget alerts. The production sender domain is cf.riveroaks.cc.
Portal application mail Migrated Current Portal code permits only the Cloudflare gateway. A production test email was received. Final-delivery analytics and a normal-cycle observation window remain required.
Expense workflow Migrated with a product decision pending Current accounting mail contains an authenticated Portal review link to stored receipts and W-9s; it does not attach the generated expense PDF. Confirm that this review-link behavior is acceptable before treating expense acceptance as complete. If an email attachment is required, add a bounded attachment design before removing MailerSend rollback.
rocc-db-local Node-RED mail Migrated The primary tracked flow uses the Cloudflare gateway only, and the August 21 live drift check passed.
MPR and MDF Node-RED Legacy cleanup in review MDF has no SendGrid node but retained an unused package. MPR retained one SendGrid node on a disabled tab plus its package. Infrastructure MR riveroaks/infra!266 removes these artifacts and adds a regression check; live deployment is still an explicit manual release.
Tasks application Production acceptance in progress A bounded, protected production email smoke job is being promoted. A successful production run and final-delivery result remain required.
Copy Room Fiery Not migrated ROCC-C5300S at 10.200.23.45 is reachable. It needs a dedicated Cloudflare Email Sending SMTP token, a real scan attachment test, and a MailerSend rollback test. Direct SMTP intentionally bypasses gateway caps so printer mail retains priority.
Bitwarden and Jamf Unverified Historical documentation says both use the shared MailerSend SMTP login. Verify their current live settings without recording credential values, then migrate or explicitly retire each sender.
BillionMail Unverified / unreachable Historical address 10.141.74.109 did not answer HTTP or HTTPS during the August 22 audit. Confirm whether the VM/service still exists and whether any live caller remains.
WordPress website Deferred, no source-code dependency found The canonical website repository contains no MailerSend or SendGrid reference. Runtime plugin configuration on 10.141.74.111 still needs a credential-safe WordPress/SMTP plugin inventory when the website project resumes.
Delivery observability Code ready; credential blocked The gateway CI now supports aggregated Cloudflare final-event counts without recipient, subject, or body data. Add a protected token scoped to Zone Analytics Read, then enable the daily job and retain its 31-day reports. Until that runs, exact current per-day delivery volume is unknown.

The canonical default-branch repository audit found no active MailerSend or SendGrid implementation in Portal, Tasks, the Cloudflare applications, the website repository, Portal API, church/mobile apps, or current supporting services. Legacy names remain in tests, historical ledgers, rollback helpers, and documentation; those references are not proof of a live delivery path.

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

  1. Assign an owner and replacement transport for every repository and external row. Mark unknown live consumers as blockers.
  2. Configure the replacement in a protected development boundary. Never clone production values into development.
  3. Add provider-neutral application configuration while keeping MailerSend as a sealed rollback path. Do not dual-send normal user mail.
  4. Exercise each workflow with reviewed test recipients. Verify recipient, sender, reply-to, links/attachments, latency, logs, and a forced failure.
  5. Move one production workflow at a time. Observe at least one normal operating cycle and confirm no unexplained MailerSend activity remains.
  6. 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.
  7. Remove MailerSend credentials from active consumers, prove old-auth rejection, and retain a time-bounded rollback record only when policy requires it.
  8. Update SPF/DKIM/DMARC deliberately and verify alignment before removing old provider DNS records.
  9. 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.