# AWS SES Production Architecture Guide

Source: https://wraps.dev/blog/ses-production-architecture

Everything you need to deploy SES at scale: dedicated IPs, bounce handling, rate limiting, and the patterns that protect your sender reputation—and keep your emails out of spam folders.

15 min read•Last updated January 2026•Wraps Team

## The Production SES Stack

Amazon SES is deceptively simple to start with—verify a domain, call the API, emails flow. But production deployments need supporting infrastructure: EventBridge routes events, SQS ensures durability, Lambda processes data, and CloudWatch monitors health. Without these, you're flying blind when deliverability drops.

Here's what a production-ready SES architecture looks like—and what Wraps deploys to your AWS account:

#### Production SES Architecture (What Wraps Deploys)

Press enter or space to select a node.You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.

Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Your application sends through SES using the `@wraps.dev/email` SDK, which routes emails through your configured IP pool. All 10 SES event types flow to EventBridge for processing, enabling real-time tracking and reputation monitoring. The configuration set `wraps-email-tracking` ties it all together.

What Wraps Deploys

One command (`wraps email init`) deploys this entire architecture to your AWS account: IAM roles with OIDC authentication, SES configuration set with all 10 event types, EventBridge rules, SQS queues with DLQ, Lambda processor, and DynamoDB for event history. All tagged with `ManagedBy: wraps-cli`.

**Before you start:** Make sure you [understand how email works](https://wraps.dev/blog/how-email-works), have proper email authentication configured. [Check your DMARC policy](https://wraps.dev/blog/your-dmarc-policy-is-useless) and [verify your DNS records](https://wraps.dev/tools) are correct before deploying production infrastructure.

## Dedicated IPs: When & Why

SES offers three IP strategies with distinct economics. Shared IPs work for most startups, but high-volume senders need dedicated IPs to isolate their reputation. On shared IPs, if another sender gets flagged for spam, your deliverability suffers too—even if you did nothing wrong.

| Strategy | Cost | Best For | Warmup Required |
| --- | --- | --- | --- |
| Shared IPs | Free | <100K emails/day, variable volume | None |
| Dedicated Standard | $24.95/IP/month | Consistent high volume | 45 days auto-warmup |
| Dedicated Managed | $15/mo + tiered | Variable volume, multiple ISPs | Automatic per-ISP |

See our [pricing page](https://wraps.dev/#pricing) for full cost breakdowns including Wraps platform features.

IP Warming Reality Check

New dedicated IPs typically need consistent daily volume per major ISP to maintain reputation—AWS recommends ramping gradually over 45 days. Drop below your established volume and the IP cools off, requiring re-warming. Managed IPs solve this by auto-routing overflow through shared pools.

### Warming Schedule

Standard dedicated IPs warm automatically over 45 days. Here's the typical progression:

| Week | Daily Volume per ISP | Total Daily |
| --- | --- | --- |
| Week 1 | 50-200 | ~1,000 |
| Week 2 | 200-1,000 | ~4,000 |
| Week 3 | 1,000-5,000 | ~20,000 |
| Week 4 | 5,000-20,000 | ~80,000 |
| Week 5-6 | 20,000-50,000 | ~200,000 |

## Configuration Set Architecture

Configuration sets let you separate email types so they don't affect each other's reputation. A promotional campaign with high unsubscribes won't hurt your password reset deliverability. Each set controls its own event tracking, IP routing, and suppression behavior. Large deployments use 3-6 configuration sets for different email types.

#### Configuration Set → IP Pool Mapping

Press enter or space to select a node.You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.

Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Wraps Configuration Set

Wraps creates a single configuration set `wraps-email-tracking` that captures all 10 event types. It includes: EventBridge destination for event routing, bounce & complaint suppression at the config set level, optional TLS enforcement, and optional custom tracking domain for branded click/open tracking URLs.

textWraps configuration set structure

Copy

```
├── wraps-email-tracking       # Wraps default config set
│   └── Event Destination: EventBridge (all 10 event types)
│   └── Suppression: Bounces + Complaints (config set level)
│   └── TLS: Required (optional)
│   └── Tracking Domain: track.yourdomain.com (optional)
│
# For advanced use cases, you can create additional sets:
├── transactional-critical    # Password resets, 2FA codes
│   └── IP Pool: dedicated-transactional
│
└── marketing-campaigns       # Newsletters, promotions
    └── IP Pool: dedicated-marketing
```

Suppression Override Gotcha

Configuration set suppression settings are _overrides_, not separate lists. Setting "disable suppression" doesn't give you a clean slate—it bypasses your entire account suppression list, potentially sending to known-bad addresses.

## Event Processing Pipeline

AWS pauses sending at **10% bounce rate** or **0.5% complaint rate**. Cross these thresholds and your account goes under review—emails stop flowing while you scramble to fix it. A robust event processing pipeline catches problems early through real-time analysis and automatic suppression.

#### Event Processing Pipeline (Wraps Architecture)

Press enter or space to select a node.You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.

Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Wraps Event Processing

Wraps captures all 10 SES event types: SEND, DELIVERY, OPEN, CLICK, BOUNCE, COMPLAINT, REJECT, RENDERING\_FAILURE, DELIVERY\_DELAY, and SUBSCRIPTION. Events are stored in DynamoDB with configurable retention (default 90 days) and automatic TTL cleanup. Suppression is handled at the configuration set level—bounced and complained addresses are automatically blocked.

| Metric | Target | Warning Alert | Critical Alert | AWS Review |
| --- | --- | --- | --- | --- |
| Bounce Rate | <2% | 2% | 4% | 5% |
| Complaint Rate | <0.05% | 0.05% | 0.08% | 0.1% |

### How Wraps Processes Events

The Lambda processor receives batches of 10 events from SQS, parses the EventBridge envelope, and stores normalized data in DynamoDB. Here's the key processing logic:

javascriptevent-processor.js

Copy

```
// Wraps Lambda Event Processor (simplified)
export async function handler(event) {
  const results = [];

  for (const record of event.Records) {
    try {
      const eventBridgeEvent = JSON.parse(record.body);
      const sesEvent = eventBridgeEvent.detail;

      // Extract common fields
      const messageId = sesEvent.mail.messageId;
      const eventType = sesEvent.eventType;
      const timestamp = new Date(sesEvent.mail.timestamp).getTime();

      // Normalize event data based on type
      const eventData = {
        messageId,
        sentAt: timestamp,
        accountId: process.env.AWS_ACCOUNT_ID,
        from: sesEvent.mail.source,
        to: sesEvent.mail.destination,
        subject: sesEvent.mail.commonHeaders?.subject,
        eventType: normalizeEventType(sesEvent),
        eventData: sesEvent,
        expiresAt: computeTTL(timestamp, RETENTION_DAYS),
      };

      await dynamodb.put({
        TableName: 'wraps-email-history',
        Item: eventData,
      });

      results.push({ itemIdentifier: record.messageId });
    } catch (error) {
      // Failed items go to DLQ after 3 retries
      console.error('Failed to process:', error);
    }
  }

  return { batchItemFailures: results };
}
```

## Rate Limiting Architecture

SES quotas use rolling 24-hour windows—your limit resets continuously, not at midnight. Default production quotas start around 50,000 emails/day and 14 emails/second. Without a buffer, a sudden spike in signups exhausts your quota and delays critical emails like password resets. Queue-based architectures absorb bursts so SES processes them at a sustainable rate.

#### Queue-Based Rate Limiting

Press enter or space to select a node.You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.

Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

### SQS + Lambda Configuration

yamlserverless.yml

Copy

```
functions:
  emailSender:
    handler: sender.handler
    reservedConcurrency: 5  # SES rate / batch size / safety factor
    events:
      - sqs:
          arn: !GetAtt EmailQueue.Arn
          batchSize: 10
          functionResponseTypes:
            - ReportBatchItemFailures

resources:
  Resources:
    EmailQueue:
      Type: AWS::SQS::Queue
      Properties:
        VisibilityTimeout: 180  # 6x Lambda timeout
        MessageRetentionPeriod: 1209600  # 14 days
        RedrivePolicy:
          deadLetterTargetArn: !GetAtt EmailDLQ.Arn
          maxReceiveCount: 5
```

Concurrency Math

If your SES rate limit is 14/sec and you process 10 emails per Lambda invocation, set `reservedConcurrency` to `(14 / 10) * 0.8 ≈ 1-2`. The 0.8 factor prevents hitting limits during retry bursts.

## CloudWatch Monitoring

Essential alarms catch reputation problems before AWS intervenes. These Terraform configurations create early-warning alerts:

hclses-alarms.tf

Copy

```
resource "aws_cloudwatch_metric_alarm" "ses_bounce_rate" {
  alarm_name          = "ses-bounce-rate-warning"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 1
  metric_name         = "Reputation.BounceRate"
  namespace           = "AWS/SES"
  period              = 300
  statistic           = "Maximum"
  threshold           = 0.02  # 2% - warns before AWS review (5%)
  alarm_actions       = [aws_sns_topic.alerts.arn]
  treat_missing_data  = "notBreaching"
}

resource "aws_cloudwatch_metric_alarm" "ses_complaint_rate" {
  alarm_name          = "ses-complaint-rate-warning"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 1
  metric_name         = "Reputation.ComplaintRate"
  namespace           = "AWS/SES"
  period              = 300
  statistic           = "Maximum"
  threshold           = 0.0005  # 0.05% - warns before AWS (0.1%) and Gmail (0.3%)
  alarm_actions       = [aws_sns_topic.alerts.arn]
  treat_missing_data  = "notBreaching"
}
```

Or configure with Wraps CLI

Skip the Terraform—enable these same alerts in one command: `wraps email upgrade` → "Enable reputation alerts". Wraps deploys 5 CloudWatch alarms with thresholds that warn _before_ AWS takes action: bounce rate at 2%/4% (vs AWS 5%/10%), complaint rate at 0.05%/0.08% (vs AWS 0.1%/0.5%), plus DLQ monitoring. You'll get email notifications when your reputation needs attention.

### Dashboard Metrics

| Widget | Metrics | Purpose |
| --- | --- | --- |
| Reputation | BounceRate, ComplaintRate | Account health at a glance |
| Delivery Funnel | Send, Delivery, Bounce, Complaint | Conversion through stages |
| Throughput | Send rate over time | Detect throttling patterns |
| ISP Breakdown | VDM metrics by domain | Per-provider performance |

## Common Mistakes That Kill Deliverability

These mistakes can wreck months of careful reputation building. We've seen them tank otherwise healthy sender accounts:

**Related:** [Why your DMARC policy is useless](https://wraps.dev/blog/your-dmarc-policy-is-useless) covers SPF/DKIM alignment in detail, and our [SPF configuration guide](https://wraps.dev/blog/spf-guide) explains the include mechanism.

#### Missing Custom MAIL FROM

SPF alignment fails without it, causing DMARC failures even with valid SPF records. See our DMARC guide for proper alignment.

#### Testing with real addresses

Use bounce@simulator.amazonses.com for testing—real bounces hurt your reputation. Learn proper testing in our sandbox guide.

#### Ignoring soft bounces

Repeatedly sending to soft-bouncing addresses signals poor list hygiene to ISPs.

#### Sudden volume spikes

ISPs interpret sudden increases as spam behavior. Scale gradually—doubling overnight is a red flag.

#### Region confusion

SES credentials, quotas, and domains are region-specific. Verified in us-east-1 ≠ verified in eu-west-1.

## Continue Learning

[

### AWS SES Sandbox Exit Guide

Step-by-step guide to getting production access with proper DNS configuration.

](https://wraps.dev/blog/ses-sandbox-guide)[

### Fix Your DMARC Policy

Most DMARC policies don't protect you. Learn what actually works.

](https://wraps.dev/blog/your-dmarc-policy-is-useless)[

### Wraps Platform Dashboard

Monitor deliverability, track events, and manage your email infrastructure.

](https://wraps.dev/platform)[

### Domain Verification Guide

Configure DKIM, SPF, and DMARC records for your sending domain.

](https://wraps.dev/docs/guides/domain-verification)

### Skip the infrastructure headaches

`wraps email init` deploys 7 AWS resources to your account: IAM roles, SES configuration, EventBridge rules, SQS queues, Lambda processor, and DynamoDB storage. All wired together and ready to send.

You own everything—no vendor lock-in. Pay only AWS pricing (~$0.10 per 1,000 emails). Don't like it? `wraps email destroy` removes everything cleanly.

[Get Started](https://wraps.dev/docs/quickstart/email)[View All Resources Deployed](https://wraps.dev/cli)

You'll need: AWS credentials and a verified domain. That's it.
