WhatsApp 22 Lakh Breakdown
PRASOON.CODES
2,277,845CONNECTIONS · ONE SERVER

1 server. 22 lakh connections.

How WhatsApp scaled with Erlang and a tiny team

In January 2012 a single WhatsApp server held more than 2.2 million open connections at the same time. This is how: the problem every chat server hits, why Erlang fits it, the bottlenecks WhatsApp's team had to hunt down, and what it all added up to by 2014.

Topic WhatsApp · Erlang · FreeBSD · scalingBased on WhatsApp's blog (2012), Rick Reed's Erlang Factory talks (2012, 2014), Erlang docs, Dan Kegel's C10K pageEdition October 2026
Contents
  1. TL;DR and key numbers
  2. Persistent connections, from zero
  3. The problem: C10K
  4. Step 1: why Erlang
  5. How a connection lives on the server
  6. Step 2: hunting the bottlenecks
  7. The result, 2012 to 2014
  8. So, what's the secret nobody tells you?
  9. Erlang vs the other approaches
  10. Try it yourself
  11. Caveats and limits
  12. FAQ
  13. Glossary and sources
01

TL;DR

2.27Mopen connections on one server, Jan 2012
10,000the C10K problem, 1999
~2.6 KBa new Erlang process (64-bit)
465Mmonthly users on ~550 servers, 2014
~35engineers at the $19B sale
02

Persistent connections, from zero

There are two ways a phone can find out it has a new message. It can ask the server every few seconds ("anything for me?"), which wastes battery and data and still arrives late. Or it can keep one connection open and let the server push the message the moment it arrives. Chat apps use the second way.

The catch is on the server. Every connected phone is an open socket the server must keep track of: some kernel memory for its buffers, a file descriptor, and whatever the application keeps per user. Most of those connections are idle most of the time (people aren't typing every second), but they all have to stay open and ready.

!

So the real question for a chat server isn't "how fast can it process messages?" but "how many mostly-idle connections can one machine hold, cheaply, while still reacting instantly when one of them speaks?"

03

The problem: C10K

In 1999, Dan Kegel's page "The C10K problem" asked how to get one server to handle ten thousand clients at the same time. That year ftp.cdrom.com was serving 10,000 clients at once on a single machine, and it was notable enough to become the page's opening example.

Why the classic way struggles

The straightforward design gives every client its own operating-system thread. It's easy to write: each thread reads, waits and writes as if it were the only client. But each thread needs its own stack, and the operating system has to schedule all of them.

≈

Kegel's example: with 2 MB stacks, a 32-bit process runs out of address space at about 512 threads (1 GB ÷ 2 MB). Many operating systems of the time struggled with more than a few hundred threads.

×

Scale it to WhatsApp (illustrative): 2,277,845 connections × 2 MB of stack each ≈ 4.5 TB of reserved stack, on a machine with about 100 GB of RAM. Real systems reserve stack lazily and can use smaller stacks, so this overstates the real cost, but it shows why "one thread per connection" doesn't reach millions. (Our arithmetic.)

The C10K page's answer was to stop dedicating a thread to each connection and instead let one thread watch many sockets with an efficient event mechanism, such as kqueue on FreeBSD or epoll on Linux. That's fast, but the code turns into callbacks and state machines that are hard to get right. Erlang offers a third path.

04

Step 1: why Erlang

Erlang was designed at Ericsson for telephone systems: millions of calls, most of them just sitting there connected, and the system must never go down. That's almost exactly the shape of a chat server.

Processes, not threads

Erlang code runs in processes that belong to the Erlang virtual machine (the BEAM), not to the operating system. They share no memory and talk only by sending each other messages. Because the VM manages them itself, they're extremely light:

327 wordsmemory of a newly spawned process (OTP 27)
2,616 Bthe same on a 64-bit machine, ≈ 2.6 KB
0 CPUused by a process waiting for a message

A process runs until it waits for a message in a receive. While it waits, the VM doesn't schedule it at all, so idle connections cost memory but no CPU. The VM runs processes on a small pool of scheduler threads, by default one per CPU core.

A waiter at every table

  • One OS thread per connection
  • Every thread has its own stack, even when its table is quiet
  • The kernel has to juggle all of them

A bell on every table

  • One tiny Erlang process per connection
  • Quiet tables cost almost nothing
  • A few waiters (schedulers, one per core) go only where a bell rings
≈

Worked out: 2,277,845 processes × 2,616 bytes ≈ 6 GB at their starting size. That fits comfortably in 100 GB, next to the kernel's socket buffers and the data each user's process grows to hold. (Our arithmetic from the Erlang docs and WhatsApp's numbers; the real per-connection cost is higher than the starting size.)

05

How a connection lives on the server

WhatsApp's server started from ejabberd, an open-source chat (XMPP) server written in Erlang, and the team later rewrote large parts of it and moved to its own protocol. The general Erlang pattern for a server like this looks like the diagram below. (WhatsApp hasn't published its exact internal design, so treat this as the model Erlang gives you, not a blueprint of their code.)

Phone

Keeps one TCP connection open to the server.

Kernel

FreeBSD holds the sockets and tells the VM which ones have data.

BEAM VM

A few scheduler threads, one per core, run whichever processes have work.

Processes

One small process per connection, asleep in receive until a message arrives.

The pattern in code

A minimal Erlang server that gives every connection its own process (an echo server, for illustration):

echo.erl · illustrative
%% one Erlang process per connection (illustrative, not WhatsApp's code)
-module(echo).
-export([start/1]).

start(Port) ->
    {ok, Listen} = gen_tcp:listen(Port, [binary, {active, false}, {reuseaddr, true}]),
    accept(Listen).

accept(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    Pid = spawn(fun() -> receive go -> loop(Socket) end end),
    ok = gen_tcp:controlling_process(Socket, Pid),
    Pid ! go,
    accept(Listen).

loop(Socket) ->
    ok = inet:setopts(Socket, [{active, once}]),
    receive                                  %% asleep here: no CPU
        {tcp, Socket, Data}  -> gen_tcp:send(Socket, Data), loop(Socket);
        {tcp_closed, Socket} -> ok
    end.

Accept

The acceptor takes a new connection and spawns a process for it (about 2.6 KB).

Hand over

The socket is handed to the new process, so its data arrives as Erlang messages to that process.

Sleep

The process waits in receive. Thousands or millions can wait at once, costing no CPU.

Wake

When data arrives, a scheduler runs just that process, it replies, and it goes back to sleep.

06

Step 2: hunting the bottlenecks

Erlang made millions of connections possible. Getting one machine there still took work. Rick Reed described the journey at Erlang Factory in March 2012: their servers started at about 200,000 connections each, and hit their first bottlenecks at about 425,000.

What was wrong: contention

Reed's summary was "contention, contention, contention". The server had 24 logical CPUs, and at high load the cores spent their time waiting in line for the same locks inside the VM and kernel (examples he gave include the timer wheel and the time-of-day lock). Adding connections stopped adding throughput because the cores were queueing instead of working.

What they changed

The climb, to scale

Bars to scale against the 2.8M peak. The 10,000 bar is so small it barely shows.

✓

The 6 January 2012 snapshot from WhatsApp's blog ("1 million is so 2011"): kern.ipc.numopensockets: 2277845 on one FreeBSD 8.x server with Erlang R14B03, 24 logical CPUs and about 100 GB of RAM, with roughly 42% of the CPU still idle.

07

The result, 2012 to 2014

Jan 2012

One server holds 2,277,845 open connections. WhatsApp's blog calls 1 million "so 2011".

Mar 2012

Reed's Erlang Factory talk: 200k → ~425k wall → 1M → 2M about a month later, with a 2.8M unplanned peak.

Feb 2014

Facebook agrees to buy WhatsApp for about $19B ($4B cash, about $12B in shares, $3B in restricted stock units). WhatsApp has 450M+ monthly users and about 35 engineers.

Mar 2014

Reed's second talk, "That's 'Billion' with a 'B'": 465M monthly users, 147M peak concurrent connections, about 550 servers and 11,000+ cores, 19B messages in and 40B out per day.

Sep 2015

Wired: about 50 engineers serving 900M users.

465Mmonthly users, 2014
147Mpeak concurrent connections
~550servers in total
~1Mphones per chat server (kept below 2M for headroom)
~10engineers on the Erlang server side
≈

Worked out: 465M users ÷ ~550 servers ≈ 845,000 users per server. 147M peak connections across about 150 chat servers ≈ 1M each, half of what one box proved it could hold in 2012. Reed put it as about 40M users per server engineer. (Our arithmetic from Reed's 2014 figures.)

08
So, what's the secret nobody tells you?
Right tool + measured engineering.

WhatsApp didn't win scale with headcount. Two choices did most of the work:

The right tool

  • Erlang's processes match "millions of mostly idle connections" almost exactly
  • Idle users cost memory but no CPU
  • Built for systems that must stay up

Measured engineering

  • They found the real limit (lock contention) by measuring
  • They fixed it at the source: VM patches, kernel tuning
  • They kept the stack simple: Erlang + FreeBSD, few moving parts
09

Erlang vs the other approaches

Today there are several good ways to hold huge numbers of connections. How they compare in general:

ApproachHow it handles connectionsTrade-off
Thread per connectionOne OS thread each; the kernel schedules themSimple code, but memory and scheduling cost grow with every connection
Event loop One thread watches many sockets with epoll / kqueueVery efficient, but logic turns into callbacks or async code; one slow handler can block others
Goroutines Lightweight runtime threads multiplexed onto OS threadsWrite simple blocking code; shared memory needs care
Erlang / Elixir processes Tiny isolated VM processes, message passing, one scheduler per coreSimple per-connection code, idle is nearly free, crashes stay contained

General characteristics, not a benchmark. Every approach on this list can reach very high connection counts with the right tuning.

10

Try it yourself

Spawn a million processes in Erlang

Install Erlang (for example brew install erlang on macOS or your Linux package manager), then start a shell with room for two million processes:

erl shell
$ erl +P 2000000
1> [spawn(fun() -> receive stop -> ok end end) || _ <- lists:seq(1, 1000000)], ok.
2> erlang:system_info(process_count).
%% MB used by all processes:
3> erlang:memory(processes) div 1000000.

A million sleeping processes take roughly 2.6 GB at their starting size (1,000,000 × 2,616 bytes), and your CPU stays quiet while they wait. That's the property WhatsApp built on.

The same in Elixir

Elixir runs on the same VM with a friendlier syntax:

iex
$ iex --erl "+P 2000000"
iex> Enum.each(1..1_000_000, fn _ -> spawn(fn -> receive do :stop -> :ok end end) end)
iex> :erlang.memory(:processes)

Check your machine's connection limits

Before a server can hold millions of sockets, the operating system has to allow it:

/ shell
# open files allowed for this shell
ulimit -n
# Linux: system-wide file limit
cat /proc/sys/fs/file-max
# FreeBSD: socket and file limits
sysctl kern.ipc.maxsockets kern.maxfiles
→

Next step: run the echo server from section 5 (save it as echo.erl, then c(echo). and echo:start(5555). in the shell) and connect a few clients with nc localhost 5555. Each client gets its own process.

11

Caveats and limits

12

FAQ

Is an Erlang process the same as an OS process?

No. It lives inside the Erlang VM, starts at about 2.6 KB, and shares nothing with other processes. The operating system only sees the VM and its few scheduler threads.

Does WhatsApp still use Erlang?

WhatsApp's server was built on Erlang through the period covered here (2012–2015). For anything after that, check WhatsApp's own engineering posts, as this breakdown only covers the sources listed.

Why FreeBSD and not Linux?

WhatsApp ran FreeBSD and tuned it heavily (kernel backports, sysctl limits). Reed's talks describe the tuning; Linux can be tuned to similar connection counts.

Can I do this with Node.js or Go?

Yes. Event loops and goroutines can also hold very large numbers of connections. Erlang's advantage is simple per-connection code with cheap idle processes and crash isolation built in.

What is C10K today?

Solved. Modern servers handle far more than 10,000 connections routinely. The term survives as shorthand for "one server, very many connections".

Should I learn Erlang or Elixir?

Elixir is the friendlier entry point today and runs on the same VM, so everything here applies. Erlang is worth reading to understand the VM itself.

13

Glossary

TCP connection
A two-way, ordered channel between two machines that stays open until one side closes it.
Socket
The operating system's handle for one connection. numopensockets counts them.
OS thread
A unit of execution scheduled by the operating system, each with its own stack.
C10K
"Concurrent 10,000": the 1999 challenge of one server handling ten thousand clients at once.
Erlang process
A tiny, isolated unit of work inside the Erlang VM that communicates only by messages.
BEAM
The Erlang virtual machine, which Elixir also runs on.
Scheduler
A VM thread (by default one per CPU core) that runs Erlang processes that have work to do.
Contention
When several cores need the same lock at once and have to wait their turn.
kqueue / epoll
FreeBSD's and Linux's ways for one thread to watch many sockets efficiently.
sysctl
A command for reading and changing kernel settings, such as socket limits.
Sources
  1. 1 million is so 2011. WhatsApp blog, 6 Jan 2012.
  2. Scaling to Millions of Simultaneous Connections. Rick Reed, Erlang Factory SF, 30 Mar 2012.
  3. How WhatsApp grew to nearly 500 million users. High Scalability summary of Rick Reed's Erlang Factory 2014 talk.
  4. The C10K problem. Dan Kegel.
  5. Erlang efficiency guide: processes, concurrent programming, erl command and FAQ. erlang.org.
  6. It all started with ejabberd. ProcessOne blog.
  7. Facebook to buy WhatsApp for up to $19B. Global News, Feb 2014.
  8. Why WhatsApp only needs 50 engineers for its 900M users. Wired, Sept 2015 (reposted on the FreeBSD forums).