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.
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.
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.
| Volume | Mailgun Foundation | Mailgun Flex | AWS SES | SES 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 API | AWS SES Equivalent | Notes |
|---|---|---|
POST /v3/{domain}/messages | SendEmail / SendRawEmail | Core send operation |
from, to, subject, text, html | Source, Destination, Message | Direct field mapping |
| API Key (Basic auth) | IAM access key or SES SMTP credentials | Use IAM for AWS-native apps |
| SMTP (smtp.mailgun.org, port 587) | email-smtp.[region].amazonaws.com, port 587 | Drop-in SMTP replacement |
h:X-Mailgun-* custom headers | MessageAttributes or raw message headers | Same capability, different syntax |
o:tag message tags | Configuration Set tags + SNS message attributes | Equivalent 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:
- Create a Rule Set in SES Receipt Rules
- Add a rule matching
@support.example.com - 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:
eu-west-1(Ireland) — primary EU region, lowest latency to EU userseu-central-1(Frankfurt) — German data residency if requiredeu-west-2(London) — UK data residency post-Brexit
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 Event | SES/SNS Event Type | Key Fields |
|---|---|---|
delivered | Delivery notification | timestamp, recipients[] |
failed (permanent) | Bounce (bounceType: Permanent) | bouncedRecipients[], bounceSubType |
failed (temporary) | Bounce (bounceType: Transient) | bouncedRecipients[] |
complained | Complaint notification | complainedRecipients[], complaintFeedbackType |
opened | Open (requires tracking enabled) | timestamp, ipAddress, userAgent |
clicked | Click (requires tracking enabled) | timestamp, link |
unsubscribed | No direct equivalent — handle in app | Implement via list-unsubscribe header |
The SNS message wrapper adds a layer around the SES event payload. Your handler needs to JSON-parse Message from the SNS envelope to get the SES event object.
Suppression List Migration
Mailgun maintains separate bounce, unsubscribe, and complaint lists per domain. SES has an account-level suppression list covering hard bounces and complaints across all domains.
Export from Mailgun:
GET /v3/{domain}/bounces # Hard bounces
GET /v3/{domain}/unsubscribes # Unsubscribes
GET /v3/{domain}/complaints # Spam complaintsImport to SES:
- Hard bounces and complaints → SES account-level suppression list (
PutSuppressedDestinationAPI, bulk via CSV import in console) - Soft bounces → track in your application, do not import to SES suppression list
- Marketing unsubscribes → maintain in your application database; SES does not manage marketing opt-outs
Migration Steps
- Verify your sending domain in SES (DKIM CNAME records + optional MAIL FROM MX)
- Request SES production access if not already active (submit AWS support case)
- Choose your region — use
eu-west-1oreu-central-1if GDPR data residency applies - Create a Configuration Set with SNS event destinations for bounces, complaints, and deliveries
- Implement SNS → Lambda event handler to process bounce and complaint notifications
- Export Mailgun suppression lists and import hard bounces/complaints to SES suppression list
- Update SMTP config or SDK calls to point at SES
- If using inbound email: replicate Mailgun routes as SES Receipt Rules
- Run parallel sends (Mailgun + SES) at low volume to validate deliverability and event handling
- Gradually shift traffic to SES — cut over fully once SNS event pipeline is validated
Where Mailgun Still Wins
- Inbound email UI: Mailgun’s routing rule interface is more intuitive than SES Receipt Rules for non-AWS users
- Simpler onboarding: No AWS account setup, IAM policies, or VPC considerations — start sending in 15 minutes
- Log search: Mailgun’s log search UI for finding specific message events is faster than querying CloudWatch Logs
- EU simplicity: Switching to Mailgun EU is a one-line endpoint change; SES requires separate regional setup
When SES is the Clear Choice
SES is the right call when your stack is AWS-native, cost is a material concern, and your team has the AWS experience to operate SNS pipelines. The $30–$300+/month savings at typical transactional volumes pay back the migration effort within one to two billing cycles.
FactualMinds is an AWS Select Tier Consulting Partner. Our AWS SES consulting service covers the full Mailgun-to-SES migration: domain setup, Receipt Rule configuration, SNS event pipeline, suppression list import, and EU region compliance. Contact us to get started.
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 Choosing the Right Cloud Platform?
Our AWS-certified architects help you evaluate cloud platforms based on your specific requirements, workloads, and business goals.
