# Migrate from Mailgun to Amazon SES

Source: https://wraps.dev/migrate/mailgun

Swapping the messages endpoint for SES is the shortest part. The three that take real time are Routes, the EU-or-US decision baked into every Mailgun domain, and moving from per-domain reputation to a rate AWS measures across your whole account unless you split the traffic into SES tenants.

Everything below is ordered so the things you wait on start first.

[Stand up SES](https://wraps.dev/docs/quickstart/email)[Mailgun vs Wraps, side by side](https://wraps.dev/compare/mailgun-vs-wraps)

## Do not move if any of these is true

Mailgun earns its price in three specific places. If you are in one of them, the arithmetic does not work in your favour.

-   Routes carry a product feature. Inbound reply handling on SES is a Region-limited receipt rule writing raw MIME to S3, and you write the parser.
-   You are grandfathered on a legacy pay-as-you-go rate. Anyone still on the old Flex pricing should price the alternative carefully before moving.
-   The validation API runs at signup. AWS has nothing comparable, so that vendor relationship survives the migration anyway.
-   You send for many customers on one account and will not pay for SES tenants. Without them SES measures bounce and complaint rates account-wide, so one bad customer can pause everybody.

## Every Mailgun feature, and what it becomes

Sorted by how much of your week it takes. The bottom three rows are the reason this migration is not an afternoon.

| Mailgun feature | Effort | What replaces it |
| --- | --- | --- |
| Messages endpoint | Swap it | SESv2 SendEmail, or the Region's SMTP endpoint |
| Sending domains | Swap it | SES verified identities, one per Region |
| Event webhooks | Some work | EventBridge or SNS, with a different payload shape |
| Log retention and search | Some work | Whatever you point the event stream at |
| Stored templates | Some work | Render in your own code, or SES templates |
| Three suppression lists | Some work | One SES list for bounces and complaints; unsubscribes are yours |
| Routes | Rebuild it | SES receipt rules, Region-limited, raw MIME only |
| Per-domain reputation | Rebuild it | Account-wide rates per Region, enforced by AWS |
| Email validation API | Rebuild it | No AWS counterpart |

## The migration, in order

Region first, exports second, AWS approval third. Code last, because code is the only part whose schedule you control.

1.  1
    
    ### Pick the AWS Region before you pick anything else
    
    Every Mailgun domain lives behind either `api.mailgun.net` or `api.eu.mailgun.net`, and you chose that when you created the domain. SES is Regional in the same way, only more so: the verified identity, the sending quota, the suppression list, and the reputation metrics all belong to one Region. Pick the Region that matches your data residency answer now, because moving later means re-verifying the domain and starting the reputation history again.
    
2.  2
    
    ### Pull all three suppression lists
    
    Mailgun splits suppressions into bounces, unsubscribes, and complaints, each with its own API endpoint and its own CSV. SES has one account-level suppression list and it only understands bounces and complaints. Your unsubscribe list has nowhere to go inside SES, which means it has to live in your application or in whatever sits on top. Losing it is the most expensive mistake available in this migration.
    
3.  3
    
    ### Inventory your Routes
    
    Routes are the Mailgun feature people forget they depend on. A route matches with an expression like `match_recipient(".*@example.com")` and then forwards, stores, or POSTs the parsed message. SES email receiving is the nearest thing and it is a different mechanism: a receipt rule set, available only in a subset of Regions, delivering raw MIME to S3, SNS, or Lambda. If you use Routes for reply handling or support inboxes, write down every one of them before you cancel anything.
    
4.  4
    
    ### Verify the domain and open production access
    
    Verify the domain in your chosen Region and publish the DKIM CNAMEs SES issues. Then file the production access request the same day. A fresh SES account is sandboxed to verified recipients, AWS usually replies inside a day and takes longer when it has questions, and it refuses thin applications. The [sandbox guide](https://wraps.dev/blog/ses-sandbox-guide) explains the constraint and the [production access guide](https://wraps.dev/docs/guides/production-access) covers what to write.
    
5.  5
    
    ### Run both SPF includes for a while
    
    Add `include:amazonses.com` alongside the Mailgun include rather than swapping them, and leave the Mailgun DKIM TXT record where it is. DMARC does not change. Watch your SPF lookup count while both are published — SPF permits ten DNS lookups and two provider includes eat into that faster than people expect. The [SPF builder](https://wraps.dev/tools/spf-builder) counts them for you.
    
6.  6
    
    ### Move the templates and the send call
    
    Mailgun templates are stored server-side with version history, and SES has no equivalent store worth migrating into. The practical move is to render in your own code and send the finished message, which is what the Wraps SDK does with React Email components living in your repository. Substitution variables do not translate on their own; a template using `%recipient.name%` has to be rewritten.
    
7.  7
    
    ### Watch the account-wide rates during cutover
    
    Move a slice of production traffic first and let it run for a week or two. The metric to watch is not the one you are used to: SES computes bounce and complaint rates across the whole account in that Region, so a single noisy domain now drags every other domain with it. When the rates hold, drop the Mailgun include and DKIM record, then close the account.
    

## What you lose

Naming these up front is cheaper than discovering them in week three.

-   Routes with expression matching. Receipt rules match recipients and hand you raw MIME, in the Regions that offer them.
-   The separate unsubscribe list. SES suppression covers bounces and complaints only.
-   Per-domain reputation. AWS aggregates bounce and complaint rates across the account, per Region.
-   Address validation. There is no AWS product for it.
-   Searchable logs by default. Events land wherever you send them, and nothing is stored until you build or buy that.
-   A support queue that owns deliverability. Production access, quota increases, and pauses are now conversations with AWS.

## The published prices, side by side

### Mailgun

Basic $15 at 10K. Foundation $35 at 50K and $75 at 100K. Scale $90 at 100K rising to $400 at 500K. Dedicated IP included from the $75/mo plans; extra IPs $59/mo each.

Flex closed to new signups in December 2025 and the legacy rate doubled to $2 per 1,000. The free tier is a hard 100/day cap, and suspensions recur in reviews.

### Amazon SES

$0.10 per 1,000 emails on à la carte. New accounts are defaulted to the Essentials plan at $0.16 per 1,000; Pro is $105/mo + $0.22 per 1,000 and Enterprise $500/mo + $0.23 per 1,000. Plans are per-account, per-Region.

Production access is an application AWS can refuse, and new accounts start sandboxed to verified addresses. No dashboard, no templates, and nothing stores your events until you build it.

Both figures come from the vendors' own pricing pages, verified August 2026. Run your real volume through the [SES calculator](https://wraps.dev/tools/ses-calculator) before you decide anything.

## Where Wraps fits

Steps four through seven are the ones with the most abandoned migrations behind them. Wraps handles those and leaves the mail itself in your account.

### What it does

-   wraps email init provisions SES, EventBridge, SQS, Lambda, DynamoDB and scoped IAM roles in the Region you choose
-   wraps email connect adopts an SES setup you already built, without modifying it
-   Bounces and complaints land in a suppression list that is checked before the next send
-   A per-message event history and a dashboard, so the log search you had does not disappear

### What it does not

-   No inbound routing. Routes have no Wraps counterpart either.
-   No address validation.
-   No influence over production access. AWS decides, normally inside a day, and can say no.
-   Contacts, templates, and workflow state live in our database, not your AWS. Sending data and delivery events are the parts that land in your account.

Platform fee only: Free ($0/mo), Pro ($29/mo), Business ($199/mo) — priced on AWS accounts, dashboard history, and governance features, not send volume. Sending is billed by AWS directly — $0.10 per 1,000 on SES à la carte, or $0.16 on the Essentials plan new AWS accounts are defaulted to. Contacts are unlimited on every tier.

## Questions people ask before they commit

### What is the SES equivalent of Mailgun Routes?

SES email receiving, configured as receipt rules. It is not a drop-in replacement. Routes match on expressions and can POST a parsed message to a URL; receipt rules match on recipient and deliver the raw MIME to S3, SNS, or Lambda, leaving the parsing to you. SES email receiving is also not offered in every Region, so confirm yours supports it before you plan a reply-handling feature around it.

### I use the EU endpoint. Which SES Region do I move to?

Any EU Region where SES is available, most commonly eu-west-1 or eu-central-1. The identity, quota, suppression list, and reputation metrics are all scoped to that one Region, so treat the choice as permanent. Check SES email receiving availability in that Region too if you rely on inbound.

### What happens to my unsubscribe list?

It has no home in SES. The SES account-level suppression list stores addresses that bounced or complained, and nothing else. Export your Mailgun unsubscribes and keep them somewhere your sending path checks before every send, whether that is your own database or a platform on top of SES.

### Does SES have anything like Mailgun's email validation API?

No. Address validation is a separate Mailgun product and AWS does not offer a counterpart. If you validate addresses at signup today, that dependency survives the migration and stays on your bill, or you replace it.

### Will my delivery rates change?

Domain reputation follows you, so mailbox providers still recognise the domain. What changes is the IP: Mailgun's pool is Mailgun's, and you start on the SES shared pool unless you buy and warm a dedicated one. For most senders the shared pool is the right default and the delivery difference is not detectable.

## Pick the Region, then start the clock

The Region choice is hard to reverse and the AWS approval is a queue. Do both this week and the rest is code you can schedule.

[Deploy SES with one command](https://wraps.dev/docs/quickstart/email)[Other Mailgun alternatives](https://wraps.dev/alternatives/mailgun)
