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
| Phase | Duration | Key Activities |
|---|---|---|
| 1. Audit | 1-3 days | Export data, inventory suppression list, catalogue integrations |
| 2. Setup | 1-2 days | Create PostMTA workspace, verify domains, configure DNS |
| 3. Warmup | 30 days | Gradually ramp sending volume on new IPs |
| 4. Shadow Run | 3-7 days | Run PostMTA in parallel; compare delivery rates |
| 5. Cutover | 1 day | Switch DNS, update integrations, decommission old provider |
| 6. Stabilise | 7-14 days | Monitor 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_reportsSave 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}/unsubscribesAmazon 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-complaintsInventory Integrations
Before cutover, catalogue every system that sends email through your legacy provider:
- Application code that calls the SMTP relay or REST API directly
- Marketing automation platforms (HubSpot, Marketo, Mailchimp)
- transactional email calls (welcome emails, password reset, receipts)
- Internal tooling with hardcoded SMTP credentials
- Third-party modules or plugins that embed provider credentials
Audit Historical Volume
Retrieve at least 90 days of sending statistics from your legacy provider:
- Average daily volume (overall and per sending domain)
- Bounce rate trend over 90 days
- Complaint rate trend over 90 days
- Inbox placement rate if available (e.g., SendGrid's inbox placement tool)
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:00ZUpload 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:
- DKIM —
TXTrecord atpmta._domainkey.yourdomain.com - SPF —
TXTrecord atyourdomain.com(or subdomain if using a subdomain for sending) - MX — only needed if you want to receive bounce notifications at a PostMTA-managed address
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
| Day | Max Daily Volume per IP | Cumulative per IP |
|---|---|---|
| 1-3 | 100 | 100-300 |
| 4-7 | 500 | 700-2,700 |
| 8-14 | 2,500 | 5,200-22,200 |
| 15-21 | 10,000 | 32,200-92,200 |
| 22-28 | 25,000 | 117,200-267,200 |
| 29-30 | 50,000 | 317,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
- Send to your best engagement segment first (recent openers/clickers) during warmup
- Avoid sending to cold or purchased lists during warmup
- Monitor engagement rates (open and click) closely during warmup — they matter more than volume
- Do not skip days during warmup; consistent sending builds reputation faster
- If bounce rates spike during warmup, pause and investigate before resuming
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 ~allDo 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=100MX 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:00ZAccepted 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 Provider | PostMTA Event |
|---|---|
SendGrid delivered | message.delivered |
SendGrid bounce | message.bounced |
SendGrid spamreport | message.complained |
SendGrid open | message.opened |
SendGrid click | message.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
- Send a test message to an external address (e.g., Gmail, Outlook) and verify it arrives in the inbox
- Confirm open tracking pixel fires when the test message is opened
- Confirm click tracking redirect works for links in the test message
- Verify bounce event is received when sending to a known-invalid address
- Verify complaint event is triggered when reporting the test message as spam in Gmail
- Check that the analytics dashboard shows the test message in the delivery funnel
- Confirm that suppressed addresses (from the imported suppression list) are rejected with the correct error response
Compare Delivery Rates
For at least 3 days post-cutover, compare PostMTA delivery rates against the historical baseline from your legacy provider. Alert thresholds:
- Deliver rate drop > 2 percentage points: investigate immediately
- Bounce rate increase > 1 percentage point: investigate immediately
- Complaint rate increase > 0.05 percentage points: investigate immediately
Rollback Procedure
When to Roll Back
Initiate rollback if any of the following are observed during or after cutover:
- Deliver rate drops by more than 5 percentage points compared to baseline
- Bounce rate spikes to more than 3x the historical average
- Complaint rate exceeds 0.5% in a 24-hour window
- Inbox placement falls below 80% for major inbox providers
Rollback Steps
- Revert DNS records (MX, SPF, DKIM) to point back to the legacy provider. Use the low TTLs set before cutover.
- Stop new message injection into PostMTA at the application level (revert integration code changes).
- Allow the legacy provider's DNS TTL to propagate (typically 5-15 minutes given the low TTL).
- Resume sending through the legacy provider for the volume that was being sent during the migration window.
- Do not decommission PostMTA workspace — keep it active for root cause analysis.
- 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.