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.

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
deliveredDelivery notificationtimestamp, recipients[]
failed (permanent)Bounce (bounceType: Permanent)bouncedRecipients[], bounceSubType
failed (temporary)Bounce (bounceType: Transient)bouncedRecipients[]
complainedComplaint notificationcomplainedRecipients[], complaintFeedbackType
openedOpen (requires tracking enabled)timestamp, ipAddress, userAgent
clickedClick (requires tracking enabled)timestamp, link
unsubscribedNo direct equivalent — handle in appImplement 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 complaints

Import to SES:

Migration Steps

  1. Verify your sending domain in SES (DKIM CNAME records + optional MAIL FROM MX)
  2. Request SES production access if not already active (submit AWS support case)
  3. Choose your region — use eu-west-1 or eu-central-1 if GDPR data residency applies
  4. Create a Configuration Set with SNS event destinations for bounces, complaints, and deliveries
  5. Implement SNS → Lambda event handler to process bounce and complaint notifications
  6. Export Mailgun suppression lists and import hard bounces/complaints to SES suppression list
  7. Update SMTP config or SDK calls to point at SES
  8. If using inbound email: replicate Mailgun routes as SES Receipt Rules
  9. Run parallel sends (Mailgun + SES) at low volume to validate deliverability and event handling
  10. Gradually shift traffic to SES — cut over fully once SNS event pipeline is validated

Where Mailgun Still Wins

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.