Scaling WebSockets the Wrong Way (On Purpose): A Multi-Instance Reality Check

FMFrank Mendez·
Scaling WebSockets the Wrong Way (On Purpose): A Multi-Instance Reality Check

You don’t truly understand distributed systems until your app “works perfectly”… and then breaks the moment you scale it.

🚧 The Illusion of “It Works on My Machine”

In Phase 1, we built a clean WebSocket chat server using Go.
Single instance. In-memory hub. Messages flow beautifully.

Everything works.

So naturally, the next step is:
“Let’s scale it.”

And this is where things start to get… interesting.


🧠 The Core Idea

Phase 2 intentionally introduces a broken architecture:

  • Two independent WebSocket servers

  • Each with its own in-memory hub

  • No shared state

  • nginx load balancing connections

At first glance, this looks like horizontal scaling.
In reality, it’s a trap.

In-memory state does not magically become distributed just because you added more containers.


🏗️ Architecture Overview

nginx (round-robin)
   ├── server-1 (hub A)
   └── server-2 (hub B)

Clients connect through nginx, but:


  • Alice and Charlie land on server-1


  • Bob lands on server-2

All three join the same room.

Sounds fine… until messages start flying.


⚙️ What Changed in This Phase?

Minimal code changes. Maximum impact.

1. Instance Awareness

Each server now identifies itself:

serverID := os.Getenv("SERVER_ID")
log.SetPrefix("[" + serverID + "] ")

This is surprisingly powerful—every log line now tells you which instance handled it.


2. /instance Endpoint

{"server_id":"server-1"}

This lets clients confirm where they’re connected.

Because debugging distributed systems without visibility is just guessing with extra steps.


3. Dockerized Multi-Instance Setup

  • server1 → :8081

  • server2 → :8082


  • nginx → :8080 (round-robin)

No shared memory. No Redis. No tricks.

Just raw isolation.


🧪 The Demo That Breaks Your Assumptions

Three clients:

Client

Instance

Expected Behavior

Alice

server-1

Sends message

Charlie

server-1

Receives ✅

Bob

server-2

Receives ❌

What Happens:

  1. Alice sends a message to room: general

  2. Charlie receives it instantly

  3. Bob… gets nothing

Timeout.

Silence.

Existential crisis.


📉 The Result

Charlie received: ✅
Bob received: ❌ NONE (timeout after 2s — expected)

And just like that:

Your “scalable” system is not actually scalable.


💥 Why This Happens

Each server has:

  • Its own memory

  • Its own connection list

  • Its own “truth”

There is no communication between instances.

So when Alice sends a message:

  • server-1 broadcasts to its clients ✔

  • server-2 has no idea anything happened ❌

This is the exact moment most developers realize:

Stateless scaling works for HTTP… but not for real-time systems.


🧩 What This Teaches You

This phase isn’t about fixing anything.

It’s about feeling the failure.

Because once you see it, you can’t unsee it.

Key takeaways:

  • Horizontal scaling ≠ shared state

  • WebSockets are inherently stateful

  • Load balancers don’t sync your data

  • In-memory hubs don’t scale beyond one instance


🧠 The Real Problem

You don’t have a WebSocket problem.

You have a distributed systems problem.

And those don’t get solved with:


  • More containers


  • More CPU


  • More hope

They get solved with shared communication layers.


🚀 What Comes Next (Phase 3)

Now that we’ve broken the system on purpose, we fix it properly:

👉 Introduce Redis Pub/Sub


  • Each instance publishes messages


  • All instances subscribe


  • Messages propagate across servers

Suddenly:


  • Alice → server-1


  • Bob → server-2


  • Charlie → server-1

…and everyone gets the message.

Like magic. Except it’s just architecture.


🧾 Final Thoughts

If your WebSocket app works perfectly in a single instance, congratulations—you’ve solved the easy part.

The real test is this:

What happens when your second server shows up?

If the answer is “everything still works,”

you’ve done it right.

If not—welcome to distributed systems.

Stay in the loop

Get notified when new posts are published. No spam, unsubscribe anytime.

No spam · Unsubscribe anytime

💬 Leave a Comment

Want to join the conversation?