How Discord scaled its backend
Millions of concurrent WebSocket connections, and the language choices that followed
Discord's core product is real-time - messages, voice, presence - which means holding open a persistent WebSocket connection per active user, at a scale of many millions of simultaneous connections, without the server-side cost spiraling out of control.
Discord's defining technical challenge is holding open a live, persistent connection (a WebSocket, not a normal request-response HTTP call) for every single active user across a very large number of communities simultaneously, so that a message or presence change (someone coming online) can be pushed to everyone who needs to see it instantly, rather than waiting for each client to ask again.
Interactive
Discord’s stack, mapped
Grouped by where each piece sits in the request path. Click through to see what each one is actually for.
Edge & delivery
Services & language
Data & messaging
Backend language/runtime
·Elixir (on the Erlang VM/BEAM)
Used for services that need to manage huge numbers of lightweight, isolated, concurrent connections - Discord's gateway servers, which hold the actual open WebSocket connections, lean on this because the underlying Erlang VM was originally built for telecom systems that needed exactly this shape of massive, fault-isolated concurrency.
Click a piece of Discord’s stack to see what it’s for
The same stack, in plain language
Elixir (on the Erlang VM/BEAM)
Backend language/runtime
Used for services that need to manage huge numbers of lightweight, isolated, concurrent connections - Discord's gateway servers, which hold the actual open WebSocket connections, lean on this because the underlying Erlang VM was originally built for telecom systems that needed exactly this shape of massive, fault-isolated concurrency.
Rust
Backend language
Used for specific, identified hot paths where Discord needed lower-level control over memory and performance than Elixir or Go gave them - notably parts of their read-states service, where garbage-collection pauses under heavy load were a measured, specific problem.
Go
Backend language
Used for a number of Discord's backend services generally, prior to and alongside the more targeted moves to Rust for specific bottlenecks.
Cassandra
Database
Discord has publicly discussed using Cassandra for message storage at very large scale, since it's built to handle huge, ever-growing write volume (billions of messages) across distributed nodes without a single database server becoming the bottleneck.
Redis
Caching / real-time state
Used for fast, temporary state that needs to be shared across many backend processes - presence status, rate limiting, and similar real-time data that benefits from an in-memory store rather than a full database round trip.
WebSockets
Real-time protocol
The actual connection protocol Discord clients use to maintain a persistent, two-way link with Discord's servers, which is what allows messages and presence changes to be pushed to a user's device instantly instead of the client having to repeatedly ask 'anything new?'
How it actually works
Elixir for the parts that need massive concurrency
Discord has written publicly about using Elixir (built on the Erlang VM, often called BEAM) for services that need to hold huge numbers of lightweight, isolated processes concurrently - a natural fit for millions of open connections. In the Erlang VM's model, each connection can be handled by its own extremely cheap, independent process, so one connection crashing or misbehaving doesn't take others down with it - a very different memory and concurrency model from a typical thread-per-connection design, and one built specifically for exactly this kind of massive fan-out.
Rewriting hot paths in Rust when a language hit its ceiling
In more than one widely-shared engineering post, Discord described specific services - including parts of their read-states and data-caching layers - being rewritten from Go or Elixir into Rust after hitting performance walls, notably garbage-collection pauses under sustained load. Garbage-collected languages periodically pause execution briefly to clean up unused memory; at Discord's scale, those pauses were becoming visible and costly for specific latency-sensitive services, and Rust - which manages memory without a garbage collector - removed that specific class of problem for those services. The pattern in their writing isn't 'Rust is universally better' - it's identifying a specific hot path with a specific measured problem, then reaching for a language suited to that one problem.
Splitting stateful, connection-heavy services from stateless ones
Discord's architecture separates services that must hold long-lived connection state (the gateway servers managing WebSocket sessions, written largely in Elixir) from stateless services that can scale and restart freely - the same statelessness-enables-scaling idea covered in this site's fundamentals, applied at a scale most systems never reach. A gateway server holding millions of live connections can't just be freely restarted the way a stateless API server can, so Discord's infrastructure treats these as genuinely different categories of service with different deployment and scaling rules.
What “microservices” means here, concretely
Discord's backend is split by function rather than being one monolith - gateway services (holding live WebSocket connections, largely Elixir), message storage and read-state services (with specific components rewritten in Rust for performance), and a range of supporting Go services, all communicating over internal networking rather than sharing a single codebase or process.
Takeaway
Discord's public writing is unusually specific about naming failure points before switching tools - the lesson isn't 'pick the fastest language,' it's 'measure the actual bottleneck before deciding what to rewrite, and only rewrite what's actually the bottleneck.'
Based on
- Discord Engineering Blog - Why Discord is switching from Go to Rust
- Discord Engineering Blog - How Discord scaled Elixir to handle millions of concurrent users
- Discord Engineering Blog - How Discord stores billions of messages
More profiles
Netflix
Designing for failure instead of hoping it won't happen
Uber
A dispatch problem that's really a real-time geospatial problem
Stripe
Money can't be 'probably correct' - reliability as the actual product
Airbnb
Breaking apart a Ruby on Rails monolith without breaking the business
Spotify
Hundreds of autonomous services, and the cloud migration that took years