Discord logo

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