Skip to main content

Mailgun to AWS SES Migration

Migrating from Mailgun to AWS SES: Step-by-Step Guide

Step-by-step migration guide for engineers moving off Mailgun — pricing breakdown, routing rule equivalents, GDPR-compliant EU region setup, and SNS-based webhook event handling.

Ask AI: ChatGPT Claude Perplexity Gemini

Mailgun has long been the go-to for developers who want a clean REST API and minimal operational overhead. AWS SES is where teams land when they prioritize cost efficiency and AWS ecosystem consolidation over out-of-the-box simplicity. If your stack is already on AWS, the migration pays for itself quickly.

Explore other technical comparisons:

Why Teams Migrate from Mailgun to SES

Cost is the immediate trigger for most migrations. Mailgun’s pricing model has shifted over the years — the legacy free tier is gone, and the current Foundation plan carries a meaningful monthly fixed cost.

VolumeMailgun FoundationMailgun FlexAWS SESSES Savings vs Foundation
10,000 emails$35/month (included)$8.00/month$1.00/month$34.00
50,000 emails$35/month (included)$40.00/month$5.00/month$30.00
100,000 emails$80/month (overage)$80.00/month$10.00/month$70.00
500,000 emails~$360/month$400.00/month$50.00/month$310.00

Mailgun Flex charges $0.80 per 1,000 emails with no monthly minimum — 8x the SES rate. Even the Foundation plan’s included 50,000 emails work out to $0.70 per 1,000 against SES’s $0.10.

API Migration: Mailgun → AWS SES

Mailgun’s Messages API and SES share the same conceptual model but use different request formats.

Mailgun APIAWS SES EquivalentNotes
POST /v3/{domain}/messagesSendEmail / SendRawEmailCore send operation
from, to, subject, text, htmlSource, Destination, MessageDirect field mapping
API Key (Basic auth)IAM access key or SES SMTP credentialsUse IAM for AWS-native apps
SMTP (smtp.mailgun.org, port 587)email-smtp.[region].amazonaws.com, port 587Drop-in SMTP replacement
h:X-Mailgun-* custom headersMessageAttributes or raw message headersSame capability, different syntax
o:tag message tagsConfiguration Set tags + SNS message attributesEquivalent for event filtering

If your application uses SMTP, the migration is a credentials swap. Update the SMTP host to email-smtp.us-east-1.amazonaws.com (or your chosen region), generate SES SMTP credentials from the SES console, and update your application config. No code changes required.

Routing Rules: Mailgun Routes → SES Receipt Rules

Mailgun Routes allow you to match inbound email by recipient pattern or domain and forward, store, or call a webhook. SES Receipt Rules provide the same capabilities with deeper AWS integration.

Mailgun route (example):

match_recipient(".*@support.example.com")
→ forward("https://app.example.com/inbound")
→ store(notify="https://app.example.com/stored")

SES Receipt Rule equivalent:

  1. Create a Rule Set in SES Receipt Rules
  2. Add a rule matching @support.example.com
  3. Set actions: Publish to SNS → Lambda function that calls your webhook endpoint

The SNS → Lambda pattern is more verbose to configure but gives you full programmatic control over inbound email routing, parsing, and processing. For teams comfortable with Lambda, it is the more powerful option.

EU Data Residency: Mailgun EU → SES eu-west-1

Mailgun offers a dedicated EU region (api.eu.mailgun.net) to keep email data within Europe for GDPR compliance. SES supports the same via region selection.

SES regions for EU data residency:

Configure your SES SDK client to use the appropriate regional endpoint. DNS verification records (DKIM CNAME, MAIL FROM MX) must be added for each region separately — you cannot share verification between SES regions.

Webhook Event Format Mapping

Mailgun fires webhook POSTs to your endpoint for each email event. SES routes events through SNS, which then calls your HTTP endpoint (via SNS HTTP subscription) or Lambda.

Mailgun EventSES/SNS Event TypeKey Fields
deliveredDeliverytimestamp, recipients[], smtpResponse, reportingMTA
failed (permanent)Bounce (Hard)bounceType: "Permanent", bouncedRecipients[].emailAddress, timestamp
failed (temporary)Bounce (Soft)bounceType: "Transient", bouncedRecipients[].emailAddress, timestamp
complainedComplaintcomplainedRecipients[].emailAddress, complaintFeedbackType, timestamp
openedOpentimestamp, ipAddress, userAgent, destination
clickedClicktimestamp, ipAddress, userAgent, link
unsubscribedNo native equivalentMust implement via List-Unsubscribe header + custom application handler
storedNo equivalentSES does not store messages; use S3 action via Receipt Rules

Important: SES events arrive wrapped in an SNS notification envelope. The Message field of the SNS notification contains a JSON string (double-encoded) of the actual SES event. Your Lambda handler must JSON.parse(event.Records[0].Sns.Message) to access the SES event object.

Email Authentication on SES: SPF, DKIM, DMARC, BIMI

Mailgun handles authentication via your verified sending domain — the dashboard publishes the SPF, DKIM, and tracking CNAMEs you need, and Mailgun signs outbound mail with their key infrastructure. SES makes you own the same surface with no managed defaults. This is good for control, and unavoidable in 2026 — Gmail, Yahoo, and Microsoft now treat unauthenticated mail from any sender above 5,000 messages per day as effectively undeliverable.

SPF. Add include:amazonses.com to your sending domain’s SPF record. If you are keeping Mailgun running in parallel during a phased cutover, your record looks like v=spf1 include:mailgun.org include:amazonses.com -all until you fully cut over. Watch the 10-DNS-lookup limit — chaining too many include: directives silently breaks SPF and is the most common deliverability regression during a Mailgun-to-SES migration.

DKIM. Enable Easy DKIM in the SES console for each verified identity and publish the three CNAME records SES generates. Easy DKIM rotates keys automatically. During the cutover window where both Mailgun and SES are sending, both DKIM keys validate independently because they use different selectors — Mailgun’s krs._domainkey.[your-domain] (or pic._domainkey.[your-domain] for newer accounts) and SES’s three CNAMEs coexist without conflict.

DMARC. Publish _dmarc.yourdomain.com with at minimum v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com on day one to start collecting aggregate reports. Move to p=quarantine after two to four weeks of clean reports, then to p=reject once every legitimate sender — transactional, marketing, internal tooling, calendar invites — is aligned. As of February 2024, Gmail and Yahoo require DMARC for any sender exceeding 5,000 messages per day; p=none meets the minimum, but enforcement is what actually blocks spoofing.

BIMI. Once you reach p=quarantine or stricter and have a Verified Mark Certificate (VMC), publish a BIMI record so your logo renders next to messages in Gmail, Apple Mail, and Yahoo. BIMI does not directly improve deliverability, but the trust signal lifts open rates measurably for transactional and lifecycle email.

Common Mailgun DNS cleanup mistakes after cutover:

Reputation, IP Warming, and List Hygiene After Mailgun

Mailgun customers on Foundation or Flex share a warmed IP pool. The reputation built on those IPs belongs to Mailgun and does not transfer to SES — you start cold the day you cut over, regardless of how clean your domain history is. Plan for it.

Migrating to SES shared IPs (most senders). The SES shared pool is suitable for transactional senders under 500,000 emails per month. There is no formal warming required, but mailbox providers still observe sending velocity per domain. Ramp gradually over 7–10 days rather than spiking from zero to full volume on day one.

Migrating to SES dedicated IPs (high-volume marketing). SES dedicated IPs cost $24.95 per IP per month — meaningfully cheaper than Mailgun’s $59/month dedicated IPs. The trade-off is that you handle warming yourself.

Day RangeDaily Volume CapNotes
Days 1–3200 emails/daySend to most engaged segment only
Days 4–71,000 emails/dayWatch bounce rate (target <2%)
Days 8–1410,000 emails/dayAdd second engagement tier
Days 15–2150,000 emails/dayMonitor complaint rate (target <0.1%)
Days 22–30Full target volumeReputation established

Keep Mailgun running in parallel for non-engaged segments during the warming window. The dual-vendor month is the cost of insurance against a deliverability dip that can take 60+ days to recover from.

List hygiene before cutover. Reputation built on a clean list is durable; reputation built on a stale list is borrowed time.

From Mailgun tags to SES Configuration Sets. Mailgun’s o:tag lets you classify sends for analytics. The SES equivalent is a Configuration Set per sending stream — transactional, marketing, lifecycle, internal — with separate event destinations and, optionally, separate dedicated IP pools. Isolating streams means a marketing reputation hit does not take down password resets.

Sending cadence and throttling. SES enforces a per-second send rate and a 24-hour quota that grow with reputation. Smooth bulk sends across hours rather than firing at minute zero — bursty patterns trigger greylisting at smaller mailbox providers and look like list-bombing to spam classifiers. Use SendBulkEmail with batched destinations and throttle at the SDK or queue layer.

Engagement-based filtering. The single highest-leverage change most teams make post-migration is to stop sending to disengaged subscribers entirely. Maintain a “send eligibility” flag in your subscriber store, updated nightly from SES open and click events, and exclude anyone who has not engaged in your defined window. Gmail and Microsoft weight recent positive engagement (opens, replies, “move to inbox”) far more heavily than total volume, so a smaller, hotter list lifts inbox placement for the entire domain — typically from the mid-80s to the mid-90s within 30 days.

Production Event Tracking: SES → Kinesis Firehose → S3 → Node.js

Mailgun gives you a polished event log with 7 days of retention on Foundation and 30 days on Flex/Growth. After cutover to SES, the SNS → Lambda pattern from the webhook section above handles tens of thousands of events per day. Past that volume, or when you need historical analytics that outlive Mailgun’s hosted window, the production-grade pipeline is SES → Kinesis Data Firehose → S3 → Node.js API.

How it works.

  1. SES Configuration Set — define one per sending stream (transactional, marketing, lifecycle) and attach a Kinesis Firehose event destination. Subscribe to all event types: send, delivery, bounce, complaint, open, click, reject, renderingFailure, deliveryDelay, and subscription.
  2. Kinesis Data Firehose — buffers events (typically 60 seconds or 5 MB) and writes newline-delimited JSON to S3 partitioned by year/month/day/hour. Enable dynamic partitioning to split by Configuration Set or sending IP for downstream filtering.
  3. S3 with lifecycle policy — Standard for 30 days (hot analytics window), Standard-IA at 30 days, Glacier Flexible Retrieval at 180 days. Raw event archives are the cheapest part of the stack and the most useful during deliverability investigations.
  4. Node.js API — consume events by tailing new S3 objects via S3 Event Notifications → SQS → Node.js worker (Express or Fastify behind an ALB, or a Lambda function for spiky traffic), or query historical data via Amazon Athena. The API exposes per-recipient timelines, per-campaign engagement, and per-domain placement metrics to your dashboards.

Why this beats Mailgun’s hosted event log.

Filtering bots, proxies, and Apple MPP. SES open events fire on pixel load and click events fire on the SES tracking redirector. Both are noisy: corporate proxies, link-scanning gateways, and Apple Mail Privacy Protection pre-fetch images and follow links, generating engagement signals that have nothing to do with a human reading the message. Without filtering, your “open rate” double-counts machine activity and your engagement-based suppression logic ends up suppressing real subscribers.

InboxEagle’s Bot Finder sits naturally between the S3 raw layer and the engagement-based filtering layer described above — it analyzes SES open and click streams, separates human engagement from bot and proxy activity (Apple MPP, Microsoft Defender, Gmail image proxy, link-scanning gateways), and feeds your Node.js API cleaned engagement data so your reputation signals reflect real subscriber behavior. Teams migrating off Mailgun’s pre-aggregated event UI underestimate how much inbox-placement signal lives in distinguishing real opens from scanner activity; a bot filter is the difference between an engagement model that improves placement and one that quietly degrades it.

Why Choose FactualMinds for Your Email Migration

FactualMinds is an AWS Select Tier Consulting Partner specializing in email infrastructure migration. We have executed SendGrid, Mailgun, Postmark, and SparkPost to AWS SES migrations and know exactly where teams get stuck.


Frequently Asked Questions

How does Mailgun compare to AWS SES?
Mailgun and SES occupy similar market positions — developer-focused, API-first transactional email platforms — but differ significantly in pricing and ecosystem fit. Mailgun Foundation costs $35/month for 50,000 emails ($0.70 per 1,000). SES costs $0.10 per 1,000, making it $5 for the same 50,000 emails. Mailgun's advantage is a simpler routing rules UI, better inbound email processing, and a cleaner developer experience out of the box. SES wins on cost at scale, deep AWS-native integration, and IAM-based access control. For teams already on AWS sending more than 30,000 emails per month, SES is almost always the economically rational choice.
Can AWS SES receive email like Mailgun?
Yes. SES Receipt Rules handle inbound email processing and are the SES equivalent of Mailgun Routes. You define conditions (recipient address patterns, domain matches) and actions (deliver to S3, trigger Lambda, forward to SNS, call a custom HTTP endpoint via SNS). The capability is equivalent to Mailgun routing, but the configuration interface is more complex — you define rules in the SES console or via CloudFormation/CDK rather than Mailgun-style regex route strings. The added complexity comes with the benefit of native integration with Lambda, S3, and SNS for inbound processing.
Is Mailgun or AWS SES better for transactional email?
For pure transactional email (order confirmations, password resets, notifications), SES is better for teams on AWS. The deliverability is comparable, IAM makes credential management simpler, and the cost at volume is dramatically lower. Mailgun has a slight edge in developer onboarding speed — you can be sending in 15 minutes without AWS account setup. Mailgun also has a more polished analytics dashboard for per-message tracking without building your own SNS event pipeline. For AWS-native teams with meaningful volume, the operational overhead of SNS event handling is worth the cost savings.
How do I migrate Mailgun suppression lists to SES?
Export your Mailgun suppression list (bounces, unsubscribes, complaints) via the Mailgun API using GET /v3/{domain}/bounces, /unsubscribes, and /complaints. For hard bounces and complaints, upload addresses to the SES account-level suppression list via the SES console or the PutSuppressedDestination API in bulk. For soft-bounce retries and application-level unsubscribe management, store the exported list in DynamoDB or your application database and check it before each send. The SES account-level suppression list automatically blocks hard bounces and complaints going forward once an address hits those thresholds.
What is the equivalent of Mailgun routing in AWS SES?
SES Receipt Rules are the equivalent of Mailgun Routes. A Receipt Rule Set contains ordered rules, each with match conditions (recipient address, domain) and one or more actions: store to S3, invoke Lambda, publish to SNS, send to WorkMail, or stop processing. The most flexible inbound pattern is SNS → Lambda: SES publishes the raw email (headers + body) to an SNS topic, and a Lambda function parses and routes it. This is more powerful than Mailgun routing for complex logic but requires more infrastructure setup. For simple forwarding, the Lambda action with a forwarding function is the practical equivalent of a Mailgun forward route.

Need Help Migrating to AWS SES?

FactualMinds is an AWS Select Tier Partner specializing in email infrastructure migration. We handle domain verification, Configuration Set architecture, bounce handling, IP warming, and cutover.