Courses › System Design › Question Breakdowns

Design Gmail

SMTP in and out, spam at the edge, mailboxes sharded by user, per-user search and change-log sync.

Design an email service like Gmail or Outlook.com: users send and receive email with the rest of the internet over SMTP, read their mailbox on web and mobile, organize with labels, search years of mail instantly, and stay protected from spam and phishing. It's a question about interoperating with SMTP, mailbox storage for billions of users, per-user search, sync across devices, and spam filtering at the edge.

Requirements

Functional

  • Users can send email to any address and receive email from anyone on the internet
  • Users can read their mailbox (threads/conversations), with labels/folders, read/unread and archive
  • Users can search their mail by sender, words, date, attachments
  • Changes sync across web, mobile and IMAP clients
  • Incoming mail is filtered for spam, phishing and malware

Out of scope: calendar, chat, contacts sync, smart replies.

Non-functional

  • Durable: an accepted email is never lost
  • Scale: ~1.5B users, ~100B+ messages/day received (most of it spam, filtered early), ~10 PB+ of mailboxes
  • Fast reads: inbox loads in < 1s; search in < 1s over years of mail
  • Highly available: receiving mail must keep working through failures (senders will retry, but not forever)
  • Private: one user's mail is never visible to another

[!TIP] Every mailbox is its own small database Email is partitioned by user almost perfectly: reads, search, labels and sync all happen within one mailbox. That makes the system horizontally scalable — shard by user ID — and pushes the hard parts to the edges: talking SMTP to the world, and stopping spam before it's stored.

Capacity estimates

Core Entities

  • Message — ID, mailbox (user), thread ID, headers, body/attachment references, labels, flags, received at
  • Thread — mailbox, subject, message IDs, last activity
  • Label — user-defined or system (Inbox, Sent, Spam)
  • Attachment blob — content hash, size, storage location (deduplicated)
  • Mailbox change log — per-user sequence of changes for sync (history ID)

API

GET  /v1/me/threads?label=INBOX&pageToken=...
GET  /v1/me/messages/{id}
POST /v1/me/messages/send   (RFC 822 message, raw or structured)
POST /v1/me/messages/{id}/modify   { "addLabels": ["Work"], "removeLabels": ["UNREAD"] }
GET  /v1/me/search?q=from:alice has:attachment after:2025/01/01
GET  /v1/me/history?startHistoryId=88123        (changes since — for sync)

# plus SMTP (port 25) inbound from other mail servers, and IMAP for third-party clients

High-Level Design

ext = client "Other Mail Servers\n(SMTP)" @0,0
mx = service "Inbound MX\n(SMTP servers)" @1,0
spam = service "Spam / Phishing /\nMalware Filters" @2,0
deliv = service "Delivery Service" @3,0
box = store "Mailbox Store\n(sharded by user)" @3,1
blob = blob "Attachment Blobs\n(dedup by hash)" @4,1
idx = store "Per-user Search Index" @4,0
user = client "Web / Mobile / IMAP" @1,2
api = service "Mail API" @2,2
out = service "Outbound SMTP\n(queue + retries)" @2,1
ext -> mx
mx -> spam
spam -> deliv
deliv -> box
deliv -> blob
deliv -> idx
user -> api
api -> box
api -> idx
api -> out : send
out --> ext
  • Inbound: MX servers accept SMTP connections, check the sender (SPF, DKIM, DMARC, reputation) and run spam, phishing and malware filters before accepting the message.
  • Delivery writes the message to the recipient's mailbox shard (and attachments to deduplicated blob storage), applies filters/labels, updates the search index, and notifies the user's devices.
  • Reading goes through the mail API to the user's shard; changes are recorded in a change log for sync.
  • Sending puts the message in the user's Sent label and an outbound queue that delivers via SMTP with retries.

Deep Dives

1. Accepting mail reliably

SMTP has built-in retries: if a server answers "temporary failure" (4xx), the sender retries for days. So the MX layer can say no when overloaded — but once it answers 250 OK, the message is our responsibility. Write it durably (replicated) before responding 250, then process it asynchronously.

2. Spam filtering at the edge

Most inbound mail is spam, so reject as early and cheaply as possible:

  • Connection level: IP reputation, rate limits, SPF/DKIM/DMARC authentication.
  • Content level: ML classifiers on headers, body, links and attachments; malware scanning in sandboxes.
  • User signals: "Report spam" and "Not spam" feed back into models and per-sender reputation.

Rejecting at SMTP time (instead of storing to the spam folder) saves the vast majority of storage.

3. Mailbox storage

Shard by user ID; within a shard, store messages in a wide-column / Bigtable-style store keyed by (userId, messageId) with label membership as secondary indexes ((userId, label, date) → messageId). Threads group messages by headers (In-Reply-To, References) plus subject heuristics. Message bodies are compressed; attachments are stored once by content hash and referenced — the same PDF sent to 1,000 employees is stored once.

4. Search

Each user's mail gets its own inverted index (partitioned with the mailbox), updated at delivery time. Queries are always scoped to one user, so they touch one shard — no global index, and no risk of cross-user leakage. Operators (from:, has:attachment, after:) map to indexed fields.

5. Sync across devices

Every change (new message, label change, read/unread) appends to the mailbox's change log with an increasing history ID. Clients store the last history ID they saw and fetch changes since it; a push notification ("mailbox changed") tells them when to sync. IMAP clients get the same through IMAP's UID/modseq mechanisms.

[!WARNING] Outbound reputation is shared If one compromised account sends a million spam emails, other providers start rejecting mail from your IPs — for everyone. Rate-limit sending per account, scan outbound mail, and isolate suspicious senders on separate IP pools.

6. Sending mail out

The outbound queue looks up the recipient domain's MX records, delivers over TLS, signs with DKIM, and retries transient failures with backoff for up to a few days, then returns a bounce. Internal recipients (same provider) skip SMTP and go straight to delivery.

What's expected at each level

  • Mid-level: SMTP in and out, a mailbox store per user, labels and threads, a search feature.
  • Senior: durable accept before 250 OK, spam filtering at the edge, sharding by user with per-user search indexes, attachment dedup, change logs for sync.
  • Staff: sender authentication (SPF/DKIM/DMARC) and reputation, outbound abuse protection, storage economics at PB/day, IMAP compatibility, and privacy boundaries enforced by per-user partitioning.