Matrix: M34
Open in panel — /app
Reference

Migration Guide

This guide walks you through migrating your email programme from a legacy provider (SendGrid, Mailgun, Amazon SES, or Postmark) to PostMTA with zero downtime and minimal risk. Follow the phases in order; each phase builds on the last.

Migration Overview

Phases

PhaseDurationKey Activities
1. Audit1-3 daysExport data, inventory suppression list, catalogue integrations
2. Setup1-2 daysCreate PostMTA workspace, verify domains, configure DNS
3. Warmup30 daysGradually ramp sending volume on new IPs
4. Shadow Run3-7 daysRun PostMTA in parallel; compare delivery rates
5. Cutover1 daySwitch DNS, update integrations, decommission old provider
6. Stabilise7-14 daysMonitor bounce rates, engagement, and reputation

Total Timeline

A typical migration takes 4 to 6 weeks from start to full cutover, with the IP warmup phase being the longest constraint. There is no way to accelerate IP warmup without risking sender reputation — major inbox providers track historical sending patterns over weeks.

Pre-Migration: Audit

Export Suppression List from Legacy Provider

SendGrid

Export blocks, bounces, and spam reports from SendGrid:

GET https://api.sendgrid.com/v3/suppression/blocks
GET https://api.sendgrid.com/v3/suppression/bounces
GET https://api.sendgrid.com/v3/suppression/spam_reports

Save each response as a CSV. Combine into a single import file with columns: recipient, reason, created_at.

Mailgun

GET https://api.mailgun.net/v3/{domain}/bounces
GET https://api.mailgun.net/v3/{domain}/complaints
GET https://api.mailgun.net/v3/{domain}/unsubscribes

Amazon SES

Export suppression list via AWS CLI:

aws sesv2 get-account-suppression-attributes
aws ses list-suppression-addresses --parameters.achieved-sending-window "90D"

Postmark

GET https://api.postmarkapp.com/filters/bounces
GET https://api.postmarkapp.com/filters/spam-complaints

Inventory Integrations

Before cutover, catalogue every system that sends email through your legacy provider:

Audit Historical Volume

Retrieve at least 90 days of sending statistics from your legacy provider:

This data determines your IP warmup starting volume and informs the expected IP pool size.

Prepare Suppression Import File

PostMTA accepts suppression imports in CSV or JSON format. The required column is recipient. Optional columns are reason and created_at.

recipient,reason,created_at
bounced_user@example.com,mailbox_not_found,2026-05-01T10:00:00Z
spam_complainer@example.net,fbl_complaint,2026-06-15T14:30:00Z

Upload the file via the dashboard under /app/suppressions or via the API.

PostMTA Setup

Create Workspace

Sign up and create a workspace. Note your workspace ID (ws_...); you will need it for API calls and SDK configuration.

Verify Sending Domains

Add each sending domain and publish the DNS records PostMTA provides. The three record types are:

Domain verification typically takes a few minutes to a few hours depending on DNS TTL settings. You can check verification status via the API:

curl https://api.postmta.com/v1/domains/{id} \
  -H "Authorization: Bearer pmta_l...xxxx"

IP Pool Sizing

Estimate how many dedicated IPs you need based on your audit data:

dedicated_ips_needed = ceiling(max_daily_volume / 50000)

Example: 200,000 emails/day requires 4 dedicated IPs. The Starter plan includes 1 shared IP; Growth includes 2 dedicated IPs; Enterprise supports custom pool sizing.

IP Warmup Strategy

Overview

When you first start sending from a new IP, inbox providers have no reputation data for that IP. Aggressive sending from a cold IP can result in poor inbox placement, temporary blocking, or throttling. IP warmup gradually builds a positive reputation by slowly increasing volume over 30 days.

30-Day Warmup Ladder

DayMax Daily Volume per IPCumulative per IP
1-3100100-300
4-7500700-2,700
8-142,5005,200-22,200
15-2110,00032,200-92,200
22-2825,000117,200-267,200
29-3050,000317,200-367,200

PostMTA automatically enforces the warmup ladder for new IPs. You do not need to manually throttle; the shaping policy applies the correct epoch-based limits. See Shaping & Warmup Policy for epoch details.

Warmup Best Practices

DNS Cutover

DNS Records to Update

After your PostMTA domains are verified, update your DNS records to point to PostMTA. The specific records depend on your current provider setup:

SPF Record Update

Replace your current SPF record with one that includes PostMTA:

// Remove old SendGrid/Mailgun entries, add PostMTA:
v=spf1 include:spf.postmta.com ~all

Do not run two SPF records simultaneously — only one SPF record per domain is valid. Update the existing record rather than adding a second one.

DKIM Record Update

Add the DKIM TXT record provided by PostMTA for your domain. If your legacy provider also has a DKIM record, remove it after the cutover is complete and PostMTA DKIM is verified.

DMARC Record Update

Update your DMARC _dmarc record to align with PostMTA as the authorized sending source. If your current DMARC record uses rua to aggregate reports to a specific address, ensure that address continues to receive reports during the transition:

v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; pct=100

MX Record (Bounce Notifications)

If you use PostMTA's bounce notification forwarding, update the MX record for the bounce-handling subdomain:

bounces.yourdomain.com.  IN  MX  10 inbound.postmta.com.

Prepare TTL Values

Before cutover, set all relevant DNS record TTLs to 300 seconds (5 minutes) to allow fast rollback if needed. Restore higher TTLs (3600-86400) 48 hours after cutover once everything is confirmed working.

Suppression List Import

Import via API

curl -X POST https://api.postmta.com/v1/suppressions/import \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@suppressions.csv" \
  -F "reason=bounced"

Supported file formats: CSV, JSON. Maximum file size: 50 MB. Large files are automatically chunked and processed asynchronously.

Import via Dashboard

Navigate to /app/suppressions, select the CSV/JSON file, choose the suppression reason, and click Import. Large imports show a progress bar and send a webhook notification on completion.

Import File Format

recipient,reason,created_at
alice@example.com,hard_bounce,2026-04-15T08:00:00Z
bob@example.net,complaint,2026-06-01T12:00:00Z
carol@example.org,unsubscribe,2026-05-20T09:30:00Z

Accepted reason values: hard_bounce, complaint, unsubscribe, manual.

Import Validation

PostMTA validates each row and skips malformed entries without failing the entire import. After import, download a validation report that shows any skipped rows with the reason for skipping.

Webhook Endpoint Migration

Register Webhook in PostMTA

curl -X POST https://api.postmta.com/v1/webhooks \
  -H "Authorization: Bearer pmta_l...xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/postmta",
    "events": [
      "message.delivered",
      "message.bounced",
      "message.complained",
      "message.opened",
      "message.clicked"
    ],
    "secret": "whsec_your_signing_secret"
  }'

Event Parity Checklist

Verify that PostMTA event types cover all the events your application currently processes from the legacy provider:

Legacy ProviderPostMTA Event
SendGrid deliveredmessage.delivered
SendGrid bouncemessage.bounced
SendGrid spamreportmessage.complained
SendGrid openmessage.opened
SendGrid clickmessage.clicked

Test Webhook Endpoint

Send a test message through PostMTA and verify that your endpoint receives and correctly processes the webhook events. PostMTA provides a webhook debugger in the dashboard that shows recent deliveries and responses.

Smoke Test After Cutover

Smoke Test Checklist

Compare Delivery Rates

For at least 3 days post-cutover, compare PostMTA delivery rates against the historical baseline from your legacy provider. Alert thresholds:

Rollback Procedure

When to Roll Back

Initiate rollback if any of the following are observed during or after cutover:

Rollback Steps

  1. Revert DNS records (MX, SPF, DKIM) to point back to the legacy provider. Use the low TTLs set before cutover.
  2. Stop new message injection into PostMTA at the application level (revert integration code changes).
  3. Allow the legacy provider's DNS TTL to propagate (typically 5-15 minutes given the low TTL).
  4. Resume sending through the legacy provider for the volume that was being sent during the migration window.
  5. Do not decommission PostMTA workspace — keep it active for root cause analysis.
  6. Document findings and open a support ticket with PostMTA if the issue is reproducible and attributable to PostMTA.

Rollback Timeline

A full rollback, including DNS propagation, typically takes 30-60 minutes. During this window, messages in PostMTA's queue will be delivered normally — they cannot be recalled. New messages submitted to PostMTA during rollback will still be delivered; stop submission at the application level to prevent double-delivery.

Double-delivery risk: During a rollback window, both providers may attempt to deliver the same message if your application does not stop submitting to PostMTA. Implement a idempotency key strategy or temporarily take your application offline during the rollback window.