Real-world system design
How WhatsApp scales, from one server to a billion users
How a messenger grows from thousands of chats to billions of them — where the resource that runs out is not requests per second but millions of connections held open at once. Based on WhatsApp's public engineering talks, in AWS terms.
Play it — turn the traffic up yourselfStage 1 of 5
Launch (~50K users)
- Mobile appMobile application
- EC2Amazon EC2
- RDSAmazon RDS
What breaks
Every phone holds a connection open all day so messages can arrive instantly, and one server can only keep so many of those alive at once.
A messenger is not request-and-reply: each phone keeps a socket open continuously so a message can be pushed the moment it arrives. The resource that runs out is not CPU per request but the number of simultaneous connections one machine can hold — its memory and file descriptors. Long before the users are many, one server hits its connection ceiling.
The fix
Run many connection servers and spread the open sockets across them, behind a layer-4 load balancer built for long-lived connections rather than short web requests. Each server holds its share — WhatsApp famously tuned single machines to millions of connections each on Erlang, but the shape is the same: shard the connections across a fleet.
A messenger scales on open connections, not requests — shard the sockets across a fleet behind a layer-4 balancer.
Based on: The WhatsApp Architecture Facebook Bought For $19 Billion (High Scalability, 2014)
Stage 2 of 5
Growth (~2M users)
- Mobile appMobile application
- NLBNetwork Load Balancer
- EC2 Auto ScalingEC2 Auto Scaling Group
- RDSAmazon RDS
What breaks
To deliver a message the server has to look up which connection server the recipient is on — and every message hitting the database for that routing is saturating it.
Once connections are spread across a fleet, sending a message means finding which server holds the recipient's socket. Doing that lookup in the database on every single message turns the router into the bottleneck — routing is a per-message read, and messages are the whole product.
The fix
Keep the presence map — which user is on which connection server — in a fast in-memory store that every server can read, rather than the database. WhatsApp did this in Mnesia, Erlang's in-memory database. Routing a message becomes a memory lookup, and the database is left for durable account data.
Who is connected where is read on every message and changes constantly — keep that presence map in memory, not the database.
Based on: How WhatsApp handles billions of messages (ByteByteGo, 2023)
Stage 3 of 5
Offline delivery (~20M users)
- Mobile appMobile application
- NLBNetwork Load Balancer
- EC2 Auto ScalingEC2 Auto Scaling Group
- MemoryDBAmazon MemoryDB for Redis
- RDSAmazon RDS
What breaks
Half the recipients are offline, so their messages have to be stored until their phone reconnects — and one database holding every undelivered message is the wall.
A message to someone whose phone is off cannot just be dropped: it has to wait, durably, until they reconnect, then be delivered in order and cleared. Funnelling every undelivered message — for hundreds of millions of users — through one database makes it the bottleneck, and the access pattern (write once, read once soon after, delete) is not what a relational primary is best at.
The fix
Give each user a durable queue of undelivered messages in a horizontally scaled store, keyed by user, that spreads the writes across many partitions instead of one primary. When the phone reconnects, its queue is drained in order and cleared. The write-once, read-once, delete pattern fits a wide-column store far better than a relational database.
Undelivered messages are millions of tiny per-user queues — store them in something that scales writes sideways, not one primary.
Based on: How WhatsApp handles billions of messages (ByteByteGo, 2023)
Stage 4 of 5
Global (~100M users)
- Mobile appMobile application
- NLBNetwork Load Balancer
- EC2 Auto ScalingEC2 Auto Scaling Group
- MemoryDBAmazon MemoryDB for Redis
- DynamoDBAmazon DynamoDB
What breaks
One region is holding connections for the whole planet — the connection fleet is stretched, and a phone on another continent pays a round-trip across the ocean on every message.
Messaging is real-time, so distance is felt directly: a message routed through a region on the far side of the world adds hundreds of milliseconds each way. A single region is both a capacity wall for the sheer number of connections and a latency wall for the people far from it.
The fix
Run the connection fleet in multiple regions and pin each phone to the nearest, so its socket and its messages are handled close to home. A cross-region layer carries messages between people in different regions, but the common case — two people near each other — never leaves the region.
Real-time messaging is latency-sensitive, so you hold each connection in the region nearest the user.
Based on: How WhatsApp handles billions of messages (ByteByteGo, 2023)
Stage 5 of 5
Planet scale (~2B users)
- Mobile appMobile application
- Route 53Amazon Route 53
- NLBNetwork Load Balancer
- EC2 Auto ScalingEC2 Auto Scaling Group
- MemoryDBAmazon MemoryDB for Redis
- DynamoDBAmazon DynamoDB
At planetary scale WhatsApp is connection fleets in every region behind geo-routing, each holding millions of open sockets, with a per-device identity so one account can be live on phone, web and desktop at once.
Based on: The WhatsApp Architecture Facebook Bought For $19 Billion (High Scalability, 2014)
Now try it yourself
Reading is one thing. Turn the traffic up on WhatsApp's architecture, watch it break, fix it — then build the whole thing from a blank canvas.
Play the scale journey