Email Service (Gmail)#
Problem statement (interviewer prompt)
Design a Gmail-scale email service: receive via SMTP, store per-user mailboxes durably, deliver outbound via SMTP, support folders/labels, full-text search, threading, attachments, IMAP/POP, and aggressive spam + phishing detection.
flowchart LR
S([Sender MTA])
RX[Inbound MTA<br/>SMTP - Postfix]
AS[Anti-spam / DKIM / SPF / DMARC]
STORE[(Mailbox Store)]
IDX[(Search Index)]
U([User])
WEB([Web / IMAP / POP])
S --> RX --> AS --> STORE
STORE --> IDX
U --> WEB --> STORE
U --> WEB --> IDX
WEB --> SMTPOUT[Outbound MTA] --> Internet
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class S,U,WEB client;
class RX,AS,SMTPOUT service;
class STORE,IDX datastore;
flowchart TB
subgraph Inbound[Inbound path]
DNS[MX records]
MTA_IN[Inbound MTA<br/>SMTP 25/465/587]
TLS[STARTTLS / MTA-STS]
AUTHV[SPF / DKIM / DMARC / ARC]
SPAM([Spam classifier<br/>Bayes + ML])
VIRUS[Malware scan]
GREY[Greylisting / rate limit]
ROUTE[Address rewriter / aliases]
end
subgraph Storage[Storage Layer]
BIG[(Bigtable / KV<br/>per-user mailbox)]
OBJ[(Attachment store)]
DEDUP[Content dedup<br/>shared blob refs]
META[(Mailbox metadata)]
LABEL[Labels / Folders model]
end
subgraph Search
IDX[(Inverted Index<br/>per user)]
REALTIME[Real-time indexer]
end
subgraph User[User access]
WEB([Web UI])
IMAP[IMAP / POP / JMAP]
API[Gmail API]
PUSH((Push - APNS / FCM))
end
subgraph Outbound
COMPOSE[Compose]
QUEUE[[Outbound queue]]
MTA_OUT[Outbound MTA pool]
DKIM_SIGN[DKIM signing]
BOUNCE[Bounce / DSN handling]
REPUTE[IP / domain reputation]
end
subgraph Features
THREAD[Threading - by Subject / Refs]
PRIO[[Priority Inbox]]
SMART[Smart Reply / Smart Compose]
PHISH[Phishing detection]
LABEL2[Filter / Rule engine]
end
subgraph Ops
QUOTA([Per-user quota])
RETN[Retention / Deletion]
AUDIT[Audit log]
end
Internet --> DNS --> MTA_IN
MTA_IN --> TLS --> AUTHV --> GREY --> SPAM --> VIRUS --> ROUTE --> BIG
BIG --> REALTIME --> IDX
BIG --> META
OBJ --- BIG
DEDUP --- OBJ
User --> WEB
WEB --> BIG
WEB --> IDX
COMPOSE --> QUEUE --> DKIM_SIGN --> MTA_OUT --> Internet
MTA_OUT --> BOUNCE
BOUNCE --> COMPOSE
REPUTE --- MTA_OUT
Features --- BIG
Ops --- BIG
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class WEB,QUOTA client;
class DNS,MTA_IN,TLS,AUTHV,VIRUS,GREY,ROUTE,DEDUP,LABEL,REALTIME,IMAP,API,COMPOSE,MTA_OUT,DKIM_SIGN,BOUNCE,REPUTE,THREAD,SMART,PHISH,LABEL2,RETN service;
class BIG,OBJ,META,IDX datastore;
class QUEUE,PRIO queue;
class SPAM compute;
class PUSH external;
class AUDIT obs;
Mailbox storage#
- Per-user mailbox in a sharded KV (Gmail historically on Bigtable; Yahoo on Cassandra-ish).
- Message keyed by
(user_id, msg_id); immutable body + mutable flags & labels. - Attachments stored once in object store, referenced by content hash (dedup).
- Search index is per-user inverted index.
Threading#
- RFC 2822
In-Reply-To/Referencesheaders form thread graph; fall back to normalized Subject. - Gmail-style label-based threading vs Outlook-style folder model.
Anti-spam stack#
- Connection-time checks (RBL, rate, greylisting).
- Auth: SPF (sender IP allowed), DKIM (signature match), DMARC (policy alignment), ARC (forwarded chain).
- Content classifier (Bayes + ML + reputation).
- User feedback ("Report spam") fed back into models.
Outbound reputation#
- Warm IPs gradually; SPF + DKIM on every send.
- Bounce processing → list hygiene.
Search#
- Real-time indexing on receive (within seconds).
- Per-user inverted index avoids cross-tenant leaks.
Scale notes#
- Billions of mailboxes; some > 10 GB.
- Attachment dedup saves significant storage (same forwarded chain).
- IMAP idle keeps connections persistent → many open sockets.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Search internals | inverted index, BM25, embeddings, ANN | search-internals |
LLD |
Immutability | immutable types, persistent collections | immutability |
Quick reference#
Functional#
- Receive (SMTP), store, deliver.
- Send via SMTP outbound.
- Per-user folders/labels; threads.
- Full-text search.
- Spam, malware, phishing detection.
- IMAP/POP for clients; web UI.
- Attachments, signatures (S/MIME, PGP optional).
Non-functional#
- 99.99% availability for inbound (loss = lost mail).
- Deliver within seconds normally; minutes during incidents.
- 1B+ mailboxes, 10s of B msgs/day.
Capacity#
- 50B msgs/day × 50 KB avg = 2.5 PB/day raw.
- Per user: avg 5-10 GB mailbox.
- Attachment dedup saves 30-50%.
Schema (per-user mailbox)#
messages(user_id, msg_id, thread_id, headers, body_ref, labels[], read)threads(user_id, thread_id, subject, last_msg_ts)index_terms(user_id, term, msg_ids[])posting list
Trade-offs#
- Per-user partitioning simplifies isolation and search but breaks aggregate analytics.
- Bigtable / Cassandra preferred over RDB at scale; transactions are per-user.
- Threading by Subject can over-merge unrelated mails; modern Gmail uses Refs.
- Server-side rules vs client filters: server-side scales but harder UX.
- End-to-end encryption (S/MIME, PGP) cripples search and spam - niche.
Refs#
- Gmail architecture talks (Bigtable + search), RFC 5321 (SMTP), RFC 5322 (Mail format), DMARC.org docs, "Gmail outage post-mortems."
FAQ#
How does an email service handle inbound SMTP?#
MX records point to an inbound MTA cluster. Postfix or a custom MTA accepts the message, runs spam and DKIM/SPF/DMARC checks, and writes the body to durable mailbox storage.
How does Gmail-scale spam filtering work?#
Layered filters combine sender reputation, DKIM/SPF/DMARC verdicts, content classifiers, URL reputation, and user-feedback signals. Borderline messages route to Spam folder.
Where are mailboxes stored at scale?#
Append-only blob storage holds message bodies. A per-user metadata DB stores labels, threading, flags, and message IDs, sharded by user with replicas for HA.
How do you build full-text email search?#
Index each message into per-user inverted indexes with terms, attachments, and labels. Sharded Elasticsearch or a custom Lucene fleet serves user-scoped queries fast.
What are DKIM, SPF, and DMARC?#
SPF authorizes sending IPs by DNS, DKIM signs message headers with a key in DNS, and DMARC tells receivers what to do when SPF or DKIM fails. Together they curb spoofing.
Related Topics#
- WhatsApp: messaging fanout cousin
- Pub-Sub Pattern: delivery substrate for notifications
- Message Queue: outbox + retry plumbing
- Notification System: shared template + fan-out design