dom4in.net
← All resources
Guide · Amazon SES

Set up Amazon SES for production email sending

Verify the domain, let SES publish DKIM through three CNAMEs, then write the one support request that gets you out of the sandbox — that request is the real gate.

The process

The whole path from a fresh AWS account to real production sending. Everything before the sandbox-exit step works in any account; the last step is where deliverability and approval are decided.

Directions

1Pick one region and stay in it

SES configuration is regional: identities, sandbox status, and sending quotas exist per region. Pick the region closest to your app (or where the rest of your stack lives) and do everything there. Verifying a domain in us-east-1 does nothing for eu-west-1.

2Create the domain identity with Easy DKIM

In the SES console open Identities, then Create identity, choose Domain, and enter your sending domain. Leave Easy DKIM on with a 2048-bit key — SES generates three CNAME records that point selectors at keys AWS rotates for you, so you never touch DKIM again after today.

Add the three CNAMEs at your DNS host. On Cloudflare set them to DNS only. The identity flips to Verified once SES sees all three, usually within minutes.

abc123._domainkey.example.com  CNAME  abc123.dkim.amazonses.com
def456._domainkey.example.com  CNAME  def456.dkim.amazonses.com
ghi789._domainkey.example.com  CNAME  ghi789.dkim.amazonses.com

3Set a custom MAIL FROM subdomain

By default the envelope sender is amazonses.com, which passes SPF but doesn't align with your domain — and alignment is what DMARC checks. Set a custom MAIL FROM like bounce.example.com in the identity settings, then add the MX and SPF records SES shows you.

This is the difference between technically-passing and actually-aligned mail. With MAIL FROM on your subdomain, both SPF and DKIM align and a p=reject DMARC policy is safe.

bounce.example.com  MX 10  feedback-smtp.us-east-1.amazonses.com
bounce.example.com  TXT    v=spf1 include:amazonses.com ~all

4Wire up bounce and complaint handling

Before asking for production access, decide what happens on a bounce or complaint — AWS asks, and suspends accounts that ignore them. The lightweight setup: create an SNS topic per event type on the identity's Notifications tab and subscribe an endpoint or queue that at minimum records the address so you stop sending to it.

A bounce rate above roughly 5% or a complaint rate above 0.1% puts the account under review, so suppressing bad addresses isn't optional hygiene — it's what keeps sending enabled.

5Request production access

New SES accounts are sandboxed: you can only send to verified addresses. In the SES console open Account dashboard and request production access. Write the request like a human explaining a legitimate use: what the mail is (transactional receipts, alerts), expected daily volume, how recipients opted in, and how bounces and complaints are handled (point at step four).

Approval typically takes a day. You start with a modest quota (often 50,000/day) that rises automatically with clean sending. Send a test to a mailbox you control and read the headers: you want spf=pass, dkim=pass, and dmarc=pass with your domain, not amazonses.com, in the alignment.

Common issues & fixes

Domain stuck in 'Verification pending'.

One of the three DKIM CNAMEs is wrong — usually a doubled domain suffix or a proxied record. All three must resolve; check each with a DNS lookup before waiting longer.

Production access request denied.

Generic requests get generic denials. Resubmit with specifics: mail types, volume, opt-in source, and your bounce/complaint handling. Mentioning a working suppression flow is usually the difference.

Mail passes SPF and DKIM but fails DMARC.

Alignment, not authentication. The envelope sender is still amazonses.com — set the custom MAIL FROM subdomain and DMARC alignment follows.

Sending works in the console test but the app gets AccessDenied.

The IAM role or user lacks ses:SendEmail / ses:SendRawEmail on that identity, or the app is calling a different region than the one you configured. Match the region first; it's the more common miss.

Account under review for bounce rate.

You're sending to stale addresses with no suppression. Enable account-level suppression in SES settings, purge known-bad addresses from your lists, and reply to the review with what changed.

Honesty note: articles are drafted with AI assistance, then a human checks each one against the vendor's current setup flow before it publishes — nothing goes live unreviewed. Vendor interfaces still change; if a step looks different, the underlying record is what matters.