Design Gmail
Receive, store, search and label a trillion emails; open any mailbox in a blink.
- Inverted indexes — From every word to the documents that contain it: how search answers in milliseconds.
- Sharding and partitioning — Splitting data by key so each machine holds a slice it can handle.
- Object and blob storage — Immutable files by name, replicated and cheap.
- Replication — Keeping copies so reads scale and a dead machine loses nothing.
- Abuse, spam and fraud signals — Finding the bad actors in the traffic without blocking the good ones.
01The design, in the six-step framework
1. Requirements
Functional
- Receive mail over SMTP for every user, send mail out, and store it durably.
- Open a mailbox and see the latest messages instantly; open any message with its attachments.
- Labels and threads instead of folders; a message may carry many labels.
- Full-text search across a user's whole mailbox.
- Spam and phishing filtering before a message reaches the inbox.
- Out of scope: the web client's rendering, calendar integration, contacts.
Non-functional
- 1.8 billion accounts; 300 billion messages received a year, about half of it spam that is dropped early.
- Inbox list in under 200 ms, p99; a message body in under 300 ms.
- Nothing accepted by SMTP is ever lost; durability across data-centre failure.
- Read-your-writes consistency: a label you just applied is visible on the next request.
- 15 GB of storage per free account; messages kept until the user deletes them.
300 × 109 ÷ (365 × 86,400) ≈ 9,500 messages received per second, about 25,000 at peak.
After spam is dropped, about 150 billion messages a year are stored. At an average of 75 KB with attachments, that is 11 PB of new mail a year; the bodies dominate, the metadata (about 1 KB per message) is 150 TB a year.
Reads: 1.8 B accounts, say 500 M daily active, each opening the inbox 10 times a day: 5 billion inbox loads a day, about 60,000 per second, each needing only metadata.
Search index: about 2 KB of postings per message, so 300 TB a year of index, kept per user.
2. Core entities
- Account: account_id, addresses, quota used, home shard.
- Message: message_id, account_id, thread_id, headers (from, to, subject, date), snippet, label set, size, body pointer.
- Body blob: content-addressed object holding the MIME body and attachments.
- Thread: thread_id, account_id, last_message_at, participant set; computed from headers.
- Label: label_id, account_id, name, colour; system labels (inbox, sent, spam) are fixed ids.
3. API
GET /mailboxes/{label}/threads?cursor=…&limit=50: the list view, newest first, metadata only. Called by the client.GET /messages/{id}: headers plus body (fetched from blob storage); attachments by separate signed URLs.POST /messages/{id}/labelsandDELETE: add or remove a label; archive is removing inbox.POST /messages: send; the service stores a copy under the sent label and hands the message to the outbound MTA.GET /search?q=…&cursor=…: full-text search within the account.- Internal: the inbound MTA calls
POST /deliverwith the raw message after spam scoring.
4. High-level design
One message arriving for Priya:
- A remote mail server connects to the inbound MTA. The MTA checks the recipient exists, applies connection-level reputation limits, and accepts the message only after writing it to a durable spool; the SMTP 250 reply is the promise of durability.
- The spam classifier scores the message using sender reputation, authentication results (SPF, DKIM, DMARC) and content signals; a high score sends it to the spam label, a very high score drops it.
- The delivery service writes the body to object storage, computes the thread_id from the headers, and writes the metadata row to Priya's home shard in the sharded metadata store with labels inbox and unread.
- It publishes an index event; the search service tokenises the subject and body and appends postings to Priya's per-account inverted index within seconds.
- Priya opens the app: the mailbox API reads the inbox threads from the metadata shard (one range read, newest first) and returns 50 rows of headers and snippets.
- She taps the message: the API reads the body pointer and streams the blob; attachments are fetched later through signed URLs.
5. Deep dives
How is the inbox list fast when a mailbox holds a million messages?
Scanning a user's messages and sorting by date is a full partition read. The metadata store is a wide-column table keyed by (account_id, label_id, reverse timestamp, message_id): the inbox for one user is a contiguous range, newest first, and 50 rows is one short scan. Applying a label is writing one row into another range; removing it is a delete. Threads are a second table keyed by (account_id, label_id, last_message_at) that the delivery service updates when a reply arrives. A per-account snippet cache in memory serves the first page for active users so that the 60,000 inbox loads a second mostly never reach disk.
Why separate bodies from metadata, and where do attachments go?
| Option | Storage | Trade-off |
|---|---|---|
| Everything in the row store | 75 KB rows, 11 PB a year in the database | Compactions and replication choke on body bytes nobody lists |
| Metadata in rows, bodies in blobs | 1 KB rows, bodies in cheap replicated object storage | Two writes per message; the body must be written first |
| Bodies deduplicated by content hash | A newsletter sent to 10 M users is stored once | Deleting is reference counting; privacy requires per-account encryption keys |
Choose metadata separate from content with dedup of identical bodies: the big win is that the hot path (lists, labels, search) touches small rows, and the cold path (bodies) scales on the cheapest storage available.
How does search answer in under a second over a 15 GB mailbox?
A global index over 1.8 billion accounts would answer every query against everyone's mail and then filter, which is both slow and a privacy risk. Instead each account owns a small inverted index, sharded with its metadata, so a query touches one machine and a few megabytes. Postings carry the message's date so ranking by recency is a merge, not a sort. The index is updated asynchronously within seconds of delivery; the API hides the gap by also checking the last minute of metadata rows for literal subject matches, which is what users notice first. Attachments are indexed by extracted text in a slower background pass.
What makes delivery durable through a data-centre failure?
The SMTP 250 is sent only after the spool write is replicated to a second zone; if the delivery service dies mid-way, the spool is replayed and the write to metadata is idempotent on message_id. The metadata store replicates each shard across three zones with synchronous majority writes, and bodies in object storage are replicated across regions asynchronously, which is acceptable because the spool holds the original until the body write is confirmed in two places. A whole-region loss means mailboxes homed there fail over to replicas with seconds of lost index updates, which the index rebuilds from metadata.
Where does spam filtering sit, and why before storage?
Half of all incoming mail is spam. Scoring before the write saves half the storage and index work and keeps abusive senders from filling mailboxes. The classifier is the abuse system's front line: cheap checks first (reputation, authentication), expensive model scoring only for the undecided middle, and user reports fed back as labels. Messages the classifier is unsure about are stored with a spam label and a 30-day expiry so a false positive can be rescued.
6. What a strong answer sounds like
- Open with the split: small hot metadata rows per account, big cold bodies in object storage, a per-account search index; everything else follows.
- Quote 25,000 messages a second at peak, 11 PB a year of bodies, 60,000 inbox loads a second, and show that each number lands on a different component.
- Explain the key design of the inbox table and why a label change is a row move, not an update.
- Expect follow-ups on multi-region homing, on exact quota accounting with dedup, and on how the outbound side avoids being used for spam.
- A common mistake is to design a generic document store and bolt on search; interviewers want the per-account partitioning that makes both list and search cheap.
02Where this question was reported
- IGotAnOffer · TPM: 'Design the server infrastructure for Gmail'
- IGotAnOffer · 'server infrastructure behind Gmail'
- DesignGurus · Listed
- DesignGurus · Listed
- System Design Handbook · Top-10
- interviewing.io · 'Design Gmail' quoted from a former Google staff engineer as a common prompt
- devshreebharatia.substack.com · L5-L6+ list.
- grokkingsystemdesigninterview.com · Most Common / L4 lists.
- Codemia · Tagged Google onsite question.