Design a Distributed Email Service
1. Problem Statement & Scope
System Mission
Design a secure, highly available, fault-tolerant distributed email service capable of managing 100 Million active mailboxes and processing over 1 Billion daily inbound and outbound messages (). The architecture must support standard wire protocols (SMTP, IMAP, POP3, and REST/JMAP), cryptographic sender verification (SPF, DKIM 2048-bit RSA, DMARC), decoupled storage tiering (DynamoDB metadata index, immutable S3 MIME/attachment blobs, OpenSearch full-text search), SQS FIFO worker queues for soft/hard bounce handling, and sub-second JWZ conversation threading.
Synthesizing vector architecture diagram...
Functional Requirements
- Inbound SMTP Message Ingestion: Accept incoming RFC 5322 MIME messages over SMTP (Port 25 with STARTTLS) via Route 53 MX records, validating SPF, DKIM, and DMARC alignment.
- Outbound Mail Delivery Engine: Queue, sign with 2048-bit DKIM private keys, resolve recipient DNS MX records, and reliably dispatch emails to external recipient hosts with automatic TLS opportunistic negotiation.
- Decoupled Storage & Large Attachment Handling: Segregate lightweight mailbox metadata () from raw MIME payloads and binary attachments (up to ), streaming attachments directly to Amazon S3.
- Sub-Second Mailbox Views & Folder Management: Support standard folders (Inbox, Sent, Drafts, Trash, Spam) with atomic unread counters, cursor-based pagination, and JWZ conversation threading.
- Full-Text Mailbox Search: Sub-300ms full-text search across email subjects, sender/recipient addresses, body contents, and extracted attachment text via Amazon OpenSearch.
- Bounce, Complaint & Dead-Letter Handling: Enforce exponential retry with jitter over 72 hours for soft bounces (4xx SMTP codes) and immediate dead-letter handling for hard bounces (5xx SMTP codes).
Non-Functional Requirements (SLAs/SLOs)
- High Availability: uptime SLA for mail access and delivery ( downtime/year).
- Data Durability: Zero-byte-loss guarantee ( durability for committed emails; 11 9s durability via Amazon S3 multi-AZ replication).
- Latency SLOs:
- Inbound End-to-End Latency: Message visible in user inbox within () of SMTP handshake completion.
- Outbound Dispatch Latency: Delivered to recipient MX in () under normal network conditions.
- Mailbox Folder Render Latency: First 50 messages retrieved in ().
- Search Query Latency: BM25 query across 100,000 user emails in ().
- CAP / PACELC Classification:
- Mailbox Metadata & Inboxes: CP / PC-EC (Consistent read/unread status, monotonic folder versioning).
- Search & Analytics: AP / PA-EL (Near real-time search index eventual consistency).
2. Capacity & Scale Estimation
Traffic Calculations
- Active Mailboxes: accounts.
- Daily Inbound & Outbound Volume: .
- Message Ingestion Throughput:
- Outbound Dispatch Throughput:
- Assuming outbound ratio ():
Storage Calculations (3-Year Horizon)
- Average Email Size Profile:
- Metadata:
- HTML & Plain Text Body:
- Attachments (averaged over all messages):
- Total Average Footprint: .
- Daily Raw Ingestion Volume:
- Storage Tier Allocation:
- DynamoDB Metadata Tier (Hot):
- Amazon S3 Raw MIME & Attachments (Immutable CAS): 3-year cumulative S3 storage . (Automated S3 Lifecycle transitions emails older than 90 days to S3 Infrequent Access, and older than 365 days to S3 Glacier Instant Retrieval, saving in storage costs).
- Amazon OpenSearch Search Inverted Index:
- Indexing sender, recipient, subject, and tokenized plain text body ( per document).
- With 1 primary shard + 1 replica: Active 90-day retention window in OpenSearch cluster.
Network Bandwidth
- Ingress / Egress Throughput:
- Outbound Egress Bandwidth:
Memory & Cache Sizing (80/20 Pareto Working Set)
- Top of users generate of mailbox access: .
- Caching the 50 most recent email headers per active user in ElastiCache Redis:
- Folder Unread Counter Cache (100M users folders ):
Fleet Sizing & Compute Provisioning
- Streaming MIME Parser Fleet (ECS Fargate):
- Streaming MIME parser processes 1 email (header demux, body extract, attachment stream) in average on 1 vCPU core.
- Required vCPUs for peak :
- Sizing: Provision 174 ECS Fargate Tasks (each configured with 4 vCPUs, 8 GB RAM).
3. AWS-First High-Level Architecture
Synthesizing vector architecture diagram...
Follow the three flows. Inbound: another mail server finds you through the MX record in Route 53 and delivers to SES, which stores the full raw message in S3 and publishes an event. In the "Streaming Parser & Decoupling Tier" panel, parsers pull the event from SQS, scan it for spam and malware, save the metadata to DynamoDB, index the text in OpenSearch, and push a notification. Reading: in the "Client Webmail & JMAP/REST API" panel, clients list folders from DynamoDB (with unread counts cached in Redis), search in OpenSearch, and fetch bodies from S3. Outbound: in the "Outbound Sending & Reputation Pipeline" panel, sent mail is queued in SQS FIFO, signed with DKIM, and delivered by SES from dedicated IPs, and bounces and complaints are fed back to suppress bad addresses. Keeping large raw messages in S3 and only metadata in DynamoDB keeps the database small and fast.
Data Flow Walkthrough
- Inbound Handshake: The external SMTP host connects to Amazon SES on port 25 with opportunistic STARTTLS. SES evaluates SPF records, DKIM signatures, and DMARC policies.
- Durability-First Blob Persist: SES streams the raw RFC 5322 MIME bytes directly into Amazon S3 before acknowledging the SMTP handshake with
250 2.0.0 OK Message Accepted. - Asynchronous Streaming Parse: SES publishes an event through SNS to SQS. ECS Fargate Parser tasks pull the task, streaming the MIME payload in ring buffers to extract headers, plain/HTML text, and binary attachments.
- Decoupled Indexing: The parser populates lightweight folder and thread records in DynamoDB (
MailboxCoreTable) and tokenizes the text into Amazon OpenSearch. An atomic increment updates the folder's unread counter. - Outbound Dispatch & Warm Pools: User send requests land in SQS FIFO queues. Outbound workers attach 2048-bit DKIM signatures and route traffic through Amazon SES Dedicated IP Pools with automated IP warming and 72-hour exponential backoff retry for 4xx soft bounces.
Concrete Step-by-Step Request Walkthrough: Tracing Inbound Mail to Inbox Render
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| 1 | External mail server opens SMTP connection; issues RCPT TO:<alice@aws.internal> | Route 53 MX resolves to SES Inbound Gateway | SES inspects sender IP against Spamhaus DNSBL; negotiates TLS 1.3 | Returns 250 2.1.5 Recipient OK |
| 2 | External sender streams RFC 5322 MIME data ( with PDF attachment) | SES verifies DKIM signature and DMARC alignment | SES streams raw bytes directly to s3://mail-raw-blobs/alice/msg_998124.eml | SES issues 250 2.0.0 OK Message accepted for delivery in |
| 3 | SES publishes event to SNS; enqueues task in Amazon SQS | SQS Parse Queue receives task: {message_id, s3_key, recipient} | Parser worker polls SQS task; acquires message lease ( visibility) | Parser acquires raw S3 byte stream |
| 4 | ECS Parser executes streaming MIME demuxing | Memory-bounded ring buffer demuxes body parts | Streams attachment directly to s3://mail-attachments/alice/att_001.pdf; extracts text | Evaluates Bayesian spam score (, Ham); runs JWZ thread matching |
| 5 | Parser executes DynamoDB TransactWriteItems | DynamoDB prepares multi-item commit | Writes FOLDER#INBOX#... item; atomically increments unread_count on FOLDER_STATS#INBOX | Metadata committed in DynamoDB in |
| 6 | Parser indexes document into Amazon OpenSearch | OpenSearch node processes inverted index update | Analyzes subject and body text with English BM25 analyzer | Document searchable in OpenSearch cluster |
| 7 | Alice opens mobile webmail; client requests GET /v1/mail/folders/INBOX/messages | Mailbox API Service queries DynamoDB MailboxCoreTable | Queries PK USER#alice with SK prefix FOLDER#INBOX# (limit 50) | Returns JSON message list with thread previews; render latency |
4. API Interface Design
1. Send Outbound Email (POST /v1/mail/messages/send)
Queues an outbound email for cryptographic signing and MX dispatch.
httpPOST /v1/mail/messages/send HTTP/1.1 Host: mail.production.aws.internal Authorization: Bearer <jwt_token> Content-Type: application/json { "to": ["architect@distributed-systems.org"], "cc": ["lead-sre@company.com"], "subject": "System Design Review: Decoupled Mailbox Storage Engine", "body_html": "<p>Team, attached is the revised architectural blueprint.</p>", "body_plain": "Team, attached is the revised architectural blueprint.", "in_reply_to": "<msg_88129a@distributed-systems.org>", "references": ["<msg_root@distributed-systems.org>", "<msg_88129a@distributed-systems.org>"], "attachments": [ { "file_name": "mail_engine_spec.pdf", "content_type": "application/pdf", "s3_temp_key": "temp-uploads/user_101/tmp_spec_pdf" } ] }
Response: 202 Accepted
json{ "message_id": "<msg_20260916_998124756@mail.aws.internal>", "thread_id": "thread_88129a_root", "status": "QUEUED_FOR_DELIVERY", "queued_at": 1773648050123 }
2. List Folder Messages (GET /v1/mail/folders/{folder}/messages)
Returns paginated message metadata with conversation threading.
httpGET /v1/mail/folders/INBOX/messages?limit=25&cursor=eyJkYXRlIjoxNzczNjQ4MDAwLCJtc2dfaWQiOiJtc2dfMTAxIn0= HTTP/1.1 Host: mail.production.aws.internal Authorization: Bearer <jwt_token>
Response: 200 OK
json{ "folder": "INBOX", "unread_count": 4, "total_count": 1420, "messages": [ { "message_id": "msg_20260916_998124756", "thread_id": "thread_88129a_root", "from": {"name": "Alice Wang", "email": "alice@cloud.org"}, "subject": "System Design Review: Decoupled Mailbox Storage Engine", "snippet": "Team, attached is the revised architectural blueprint...", "has_attachments": true, "is_read": false, "received_at": 1773648050000, "s3_mime_key": "mail-raw-blobs/user_101/2026/09/msg_998124.eml" } ], "next_cursor": "eyJkYXRlIjoxNzczNjQ3ODAwLCJtc2dfaWQiOiJtc2dfMDk5In0=" }
3. Status Codes & Error Contracts
| HTTP Status | Reason Code | Error Contract Payload | Mitigation / Client Action |
|---|---|---|---|
200 OK | SUCCESS | Entity payload / folder list | Normal completion |
202 Accepted | MAIL_DISPATCH_QUEUED | Message ID and queued timestamp | Client displays message in Sent folder |
400 Bad Request | INVALID_RECIPIENT | {"error": "Malformed email address syntax"} | Prompt user to correct address |
404 Not Found | MESSAGE_NOT_FOUND | {"error": "Message ID does not exist"} | Refresh mailbox list view |
413 Payload Large | ATTACHMENT_LIMIT_EXCEEDED | {"error": "Total attachments exceed 25MB"} | Convert to cloud drive link share |
429 Too Many Req | OUTBOUND_QUOTA_EXCEEDED | {"error": "Daily sending limit of 500 reached"} | Backoff; alert account owner |
(async, not HTTP) SMTP 550 from recipient MX | HARD_BOUNCE_REJECTED | Delivered later as an SNS bounce event and a delivery_status: "BOUNCED" on the Sent item, because the POST /send call has already returned 202 | Suppress address; do not retry |
5. Data Models & Storage Architecture
Physical Storage Layout: Decoupled Mail Blobs in Amazon S3
| Bucket | Key path | What it holds |
|---|---|---|
s3://mail-raw-blobs/ | {user_id}/{year}/{month}/{day}/{message_id}.eml | Immutable RFC 5322 MIME stream (encrypted with AWS KMS) |
s3://mail-attachments/ | {user_id}/{message_id}/att_01_spec.pdf | First attachment of the message |
s3://mail-attachments/ | {user_id}/{message_id}/att_02_diagram.png | Second attachment of the message |
DynamoDB Single-Table Schema (MailboxCoreTable)
Partition Key (PK) | Sort Key (SK) | Attributes & Payloads | GSI1-PK / GSI1-SK |
|---|---|---|---|
USER#<user_id> | FOLDER#INBOX#DATE#<timestamp>#MSG#<msg_id> | thread_id, from, subject, is_read: false, s3_key, has_att: true | THREAD#<thread_id> / DATE#<timestamp> |
USER#<user_id> | FOLDER#SENT#DATE#<timestamp>#MSG#<msg_id> | thread_id, to: [...], subject, s3_key, delivery_status: "DELIVERED" | THREAD#<thread_id> / DATE#<timestamp> |
USER#<user_id> | FOLDER_STATS#INBOX | unread_count: 14, total_count: 1250, updated_at: 1773648050 | — |
USER#<user_id> | MSG_DETAIL#<msg_id> | s3_mime_key, body_preview, headers_json, attachments_manifest | — |
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~38%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.