What is Amazon SES and How to Verify a Domain for Sending Emails
If you're building a production application that needs to send transactional emails from a custom domain like myapp@mycompany.com, Amazon SES (Simple Email Service) is the AWS-native path — but the domain verification and DNS setup trips up most engineers the first time. This guide walks through exactly which DNS records SES requires, why each one exists, and how to confirm everything is wired correctly from the CLI.
TL;DR: Amazon SES Domain Verification Summary
| DNS Record Type | Purpose | Required? |
|---|---|---|
| 3× CNAME (Easy DKIM) | Domain ownership verification + DKIM signing | Yes — mandatory |
| TXT (SPF) | Authorize SES to send on your domain's behalf | Strongly recommended |
| CNAME (DMARC) | Policy enforcement + deliverability reporting | Strongly recommended |
| MX (custom MAIL FROM) | Bounce handling via custom subdomain | Optional but best practice |
When you enable Easy DKIM, SES generates three CNAME records. Publishing those CNAMEs both verifies your domain and enables DKIM signing — no separate TXT verification record is needed in SES v2.
How Amazon SES Domain Verification Works
Amazon SES operates as a managed SMTP relay. Before it will send email on behalf of your domain, it must confirm you control that domain. In SES v2, the verification mechanism is bundled into Easy DKIM: SES generates an RSA key pair, publishes the public key internally, and gives you three CNAME records to add to your DNS. When SES detects those CNAMEs resolving correctly, it marks the identity as verified and begins signing outbound messages with DKIM automatically. There is no separate TXT-based domain verification step in SES v2 — the CNAME propagation serves both purposes simultaneously.
- Identity creation: You register your domain as an email identity in SES. SES generates three DKIM CNAME tokens unique to your domain.
- DNS publication: You add those CNAMEs to your DNS provider (Route 53, Cloudflare, etc.).
- SES polling: SES periodically queries DNS for the CNAMEs. Once all three resolve, the identity status transitions to
VERIFIED. - DKIM signing: Every outbound message is automatically signed using the private key SES holds, referencing the public key via the CNAME chain.
- SPF + DMARC: These are separate DNS records you add independently to complete the authentication stack.
Prerequisites
- AWS CLI v2 installed and configured (
aws configure) - IAM permissions to call
sesv2:CreateEmailIdentityandsesv2:GetEmailIdentity - Access to your domain's DNS management console
- SES sandbox status noted — new accounts start in the sandbox and can only send to verified addresses until production access is granted
🔽 Minimum IAM policy for SES domain verification
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SESIdentityManagement",
"Effect": "Allow",
"Action": [
"sesv2:CreateEmailIdentity",
"sesv2:GetEmailIdentity",
"sesv2:ListEmailIdentities",
"sesv2:DeleteEmailIdentity"
],
"Resource": "arn:aws:ses:us-east-1:123456789012:identity/*"
}
]
}
Step-by-Step: Verifying Your Domain in Amazon SES
Step 1: Create the Email Identity and Retrieve DKIM CNAME Records
Registering the domain with SES is the action that triggers key generation. Until you do this, there are no CNAME values to publish — so this must come before any DNS changes. The command below creates the identity with Easy DKIM enabled at RSA-2048 key length, which is the current recommended minimum.
aws sesv2 create-email-identity \
--email-identity mycompany.com \
--dkim-signing-attributes NextSigningKeyLength=RSA_2048_BIT \
--region us-east-1
Now retrieve the three CNAME records SES generated:
aws sesv2 get-email-identity \
--email-identity mycompany.com \
--region us-east-1 \
--query 'DkimAttributes.Tokens' \
--output text
🔽 Example output (three DKIM tokens)
abc123def456ghi789 jkl012mno345pqr678 stu901vwx234yz5678
Each token maps to one CNAME record in the format below. Replace TOKEN with each value and mycompany.com with your domain:
TOKEN._domainkey.mycompany.com CNAME TOKEN.dkim.amazonses.com
# Example with first token:
abc123def456ghi789._domainkey.mycompany.com CNAME abc123def456ghi789.dkim.amazonses.com
Add all three CNAME records to your DNS provider. TTL of 300–3600 seconds is typical. SES will not verify until all three are resolvable.
Step 2: Add the SPF TXT Record
DKIM proves the message wasn't tampered with, but SPF tells receiving mail servers that SES is an authorized sender for your domain. Without SPF, messages may pass DKIM but still be treated with suspicion by strict receivers. If your domain already has an SPF record, add the SES include rather than creating a duplicate TXT record — multiple SPF records on the same name cause an SPF permerror.
# DNS TXT record for SPF
Name: mycompany.com
Type: TXT
Value: "v=spf1 include:amazonses.com ~all"
Think of SPF as the guest list at the door: it tells the bouncer which mail servers are allowed to claim they're from your domain. DKIM is the wax seal on the envelope — it proves the content wasn't altered in transit. You need both.
Step 3: Add the DMARC TXT Record
DMARC ties SPF and DKIM together under a policy your domain publishes. Without a DMARC record, even authenticated messages may be handled inconsistently across large receivers like Gmail and Outlook. Start with p=none to collect reports without affecting delivery, then tighten to quarantine or reject once you've confirmed your mail streams are clean.
# DNS TXT record for DMARC
Name: _dmarc.mycompany.com
Type: TXT
Value: "v=DMARC1; p=none; rua=mailto:dmarc-reports@mycompany.com"
Step 4: (Optional) Configure a Custom MAIL FROM Domain
By default, SES uses its own subdomain for the envelope sender (the MAIL FROM address used during SMTP negotiation). Configuring a custom MAIL FROM subdomain like mail.mycompany.com makes SPF alignment pass under DMARC strict mode, which is required if you ever move to p=reject. This step adds an MX record and a second SPF TXT record on the subdomain — not on the apex domain.
aws sesv2 put-email-identity-mail-from-attributes \
--email-identity mycompany.com \
--mail-from-domain mail.mycompany.com \
--behavior-on-mx-failure USE_DEFAULT_VALUE \
--region us-east-1
Then add these two DNS records for the subdomain:
# MX record — routes bounces back to SES
Name: mail.mycompany.com
Type: MX
Value: 10 feedback-smtp.us-east-1.amazonses.com
# SPF TXT record for the MAIL FROM subdomain
Name: mail.mycompany.com
Type: TXT
Value: "v=spf1 include:amazonses.com ~all"
Step 5: Confirm Verification Status
DNS propagation typically takes minutes but can take up to 72 hours depending on your registrar and TTL settings. Poll the identity status until VerifiedForSendingStatus returns true — that's the definitive signal that SES has confirmed all three CNAMEs and the domain is ready to send.
aws sesv2 get-email-identity \
--email-identity mycompany.com \
--region us-east-1 \
--query '{
SendingEnabled: SendingAttributes.SendingEnabled,
VerifiedForSending: VerifiedForSendingStatus,
DkimStatus: DkimAttributes.Status
}'
🔽 Expected output when fully verified
{
"SendingEnabled": true,
"VerifiedForSending": true,
"DkimStatus": "SUCCESS"
}
If DkimStatus shows PENDING after several hours, the most common cause is a CNAME published with the domain name appended twice — for example, abc123._domainkey.mycompany.com.mycompany.com. Some DNS providers auto-append the zone name; use a relative hostname (without the trailing domain) in that case.
Complete DNS Records Reference
→ token1.dkim.amazonses.com"] A --> C["CNAME: token2._domainkey
→ token2.dkim.amazonses.com"] A --> D["CNAME: token3._domainkey
→ token3.dkim.amazonses.com"] A --> E["TXT @ apex
v=spf1 include:amazonses.com ~all"] A --> F["TXT _dmarc.mycompany.com
v=DMARC1; p=none; rua=..."] A --> G["Optional: mail.mycompany.com"] G --> H["MX: feedback-smtp.us-east-1.amazonses.com"] G --> I["TXT: v=spf1 include:amazonses.com ~all"] B & C & D --> J["Domain Verified + DKIM Enabled"] E & F --> K["SPF + DMARC Authentication"] H & I --> L["Custom MAIL FROM
SPF Alignment"]
- Easy DKIM CNAMEs (×3): Generated by SES, unique per domain. These verify ownership and enable DKIM signing.
- SPF TXT on apex: Authorizes SES infrastructure to send as your domain.
- DMARC TXT on _dmarc subdomain: Publishes your authentication policy to receiving servers.
- MX on mail subdomain (optional): Routes bounces to SES feedback endpoint for custom MAIL FROM.
- SPF TXT on mail subdomain (optional): Required for DMARC strict SPF alignment when using custom MAIL FROM.
Experience Signal: The Misdiagnosis That Costs Hours
A common failure pattern: you publish all three CNAMEs, wait an hour, check the SES console, and the domain still shows PENDING. The instinct is to blame DNS propagation and wait longer. The actual cause, in roughly half of these cases, is a double-suffix problem.
When you add a CNAME in Route 53, you enter the record name as abc123._domainkey and Route 53 appends .mycompany.com automatically. But in Cloudflare, GoDaddy, and several other providers, if you enter the full name abc123._domainkey.mycompany.com, the provider also appends the zone suffix — resulting in abc123._domainkey.mycompany.com.mycompany.com. SES queries the correct name and gets NXDOMAIN.
The fix is to verify what actually resolved before blaming propagation:
# Verify what your DNS is actually serving
dig CNAME abc123def456ghi789._domainkey.mycompany.com +short
# Expected output:
abc123def456ghi789.dkim.amazonses.com.
If dig returns nothing or an unexpected value, the record name is wrong — not the propagation timing. Fix the record name first, then wait for propagation.
DNS propagation is rarely the problem. Incorrect record names are. Always verify with dig before assuming a timing issue.
Depth Signal: DMARC Alignment and the Custom MAIL FROM Dependency
Here's the interaction that catches engineers who've done everything "correctly" by the docs: you have SPF passing, DKIM passing, and a DMARC record published — but DMARC still fails alignment under strict mode.
DMARC alignment requires that the domain in the From: header matches either the SPF envelope sender domain or the DKIM d= tag. When you use SES without a custom MAIL FROM, the envelope sender domain is an SES-owned subdomain (e.g., amazonses.com), not mycompany.com. SPF passes for amazonses.com, but DMARC alignment against mycompany.com fails because the domains don't match.
DKIM alignment still passes because SES signs with d=mycompany.com. So under p=none this is invisible — DMARC evaluates as passing via DKIM. But if you ever move to p=reject and SPF alignment breaks for any reason (forwarded messages, misconfigured relay), you have no SPF fallback. Configuring the custom MAIL FROM subdomain in Step 4 closes this gap by making the envelope sender domain mail.mycompany.com, which aligns with mycompany.com under relaxed mode.
Sending a Test Email After Amazon SES Domain Verification
Once VerifiedForSendingStatus is true, send a test message to confirm the full pipeline. In the sandbox, the destination address must also be a verified identity.
aws sesv2 send-email \
--from-email-address myapp@mycompany.com \
--destination ToAddresses=verified-test@example.com \
--content '{
"Simple": {
"Subject": {"Data": "SES domain verification test"},
"Body": {"Text": {"Data": "Domain verified and sending confirmed."}}
}
}' \
--region us-east-1
Check the response for a MessageId. If you receive a MessageRejected error citing sandbox restrictions, you need to request production access via the SES console under Account dashboard → Request production access.
Wrap-Up and Next Steps for Amazon SES
Domain verification in Amazon SES v2 requires publishing three Easy DKIM CNAME records — those CNAMEs handle both ownership verification and DKIM signing in a single step. SPF and DMARC are separate DNS records you add independently to complete a production-grade email authentication stack. The custom MAIL FROM configuration is optional but closes an SPF alignment gap that becomes relevant under strict DMARC policy.
From here, the logical next steps are:
- Request SES production access to remove sandbox sending restrictions
- Configure SES sending quotas and suppression lists to protect your domain reputation
- Set up SNS notifications for bounces and complaints via SES event destinations
- Review the Amazon SES Developer Guide for configuration sets and dedicated IP options
Glossary
| Term | Definition |
|---|---|
| Easy DKIM | SES-managed DKIM configuration where AWS holds the private key and you publish three CNAME records pointing to AWS-hosted public keys. |
| SPF (Sender Policy Framework) | A DNS TXT record that lists mail servers authorized to send email on behalf of a domain. |
| DMARC | A DNS policy record that instructs receiving servers how to handle messages that fail SPF or DKIM alignment checks. |
| MAIL FROM domain | The envelope sender domain used during SMTP negotiation, distinct from the From: header visible to recipients. |
| SES Sandbox | The default restricted sending mode for new SES accounts, limiting outbound mail to verified addresses only. |
Comments
Post a Comment