<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashwin k</title>
    <description>The latest articles on DEV Community by Ashwin k (@ashwin_k).</description>
    <link>https://dev.to/ashwin_k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1972526%2F718c1085-5c59-45d4-af92-defecbc5d0ec.jpeg</url>
      <title>DEV Community: Ashwin k</title>
      <link>https://dev.to/ashwin_k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashwin_k"/>
    <language>en</language>
    <item>
      <title>How I Fixed Multi-Pod WebSocket Sync in Kubernetes Using Redis Adapter</title>
      <dc:creator>Ashwin k</dc:creator>
      <pubDate>Mon, 23 Mar 2026 09:09:49 +0000</pubDate>
      <link>https://dev.to/ashwin_k/how-i-fixed-multi-pod-websocket-sync-in-kubernetes-using-redis-adapter-30p6</link>
      <guid>https://dev.to/ashwin_k/how-i-fixed-multi-pod-websocket-sync-in-kubernetes-using-redis-adapter-30p6</guid>
      <description>&lt;p&gt;If you've ever deployed a Socket.IO app to Kubernetes and watched your real-time features silently break — no errors, no crashes, just messages vanishing — this is exactly what happened to me, and here's how I fixed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;We had a real-time feature built with WebSockets (Socket.IO). Everything worked perfectly in local and single-instance environments.&lt;/p&gt;

&lt;p&gt;The moment we deployed to Kubernetes with &lt;strong&gt;multiple pods&lt;/strong&gt;, things broke.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptoms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Users connected to different pods couldn't receive each other's events&lt;/li&gt;
&lt;li&gt;Messages randomly "disappeared"&lt;/li&gt;
&lt;li&gt;Broadcasting only worked within the same pod&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, everything looked fine — no errors, no crashes. But the system was fundamentally broken at the architectural level.&lt;/p&gt;




&lt;h2&gt;
  
  
  Root Cause: In-Memory Connections Don't Cross Pod Boundaries
&lt;/h2&gt;

&lt;p&gt;Socket.IO (and WebSockets in general) maintain &lt;strong&gt;in-memory connections&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each pod has its own isolated set of connected clients&lt;/li&gt;
&lt;li&gt;There is &lt;strong&gt;no shared state between pods&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when User A connects to Pod 1 and User B connects to Pod 2, and Pod 1 emits an event — Pod 2 has &lt;strong&gt;no idea that event exists&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each pod becomes a real-time island.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Architecture Problem
&lt;/h2&gt;

&lt;p&gt;Kubernetes distributes traffic using a load balancer. Requests get routed to different pods randomly — and without a shared communication layer, those pods can never talk to each other.&lt;/p&gt;

&lt;p&gt;Without a &lt;strong&gt;shared messaging layer&lt;/strong&gt;, real-time systems break horizontally.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Redis Pub/Sub + Socket.IO Adapter
&lt;/h2&gt;

&lt;p&gt;To fix this, we introduced &lt;strong&gt;Redis Pub/Sub&lt;/strong&gt; using the official Socket.IO Redis adapter.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Redis does here
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Acts as a &lt;strong&gt;message broker&lt;/strong&gt; between all pods&lt;/li&gt;
&lt;li&gt;When Pod 1 emits an event → it's &lt;strong&gt;published to Redis&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Redis &lt;strong&gt;broadcasts&lt;/strong&gt; it to all subscribed pods&lt;/li&gt;
&lt;li&gt;Every pod then emits it to its own connected clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: &lt;strong&gt;All clients receive the event, regardless of which pod they're on.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1auin5bey13oghehs26m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1auin5bey13oghehs26m.png" alt="Broken Multi Pod Architecture" width="680" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqptfa3r79edjhxb31ij.png" alt="Redis Adapter fix Architecture" width="680" height="340"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;socket.io @socket.io/redis-adapter ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create Redis pub/sub clients
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pubClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redis-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pubClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two separate clients are required — one for publishing, one for subscribing. This is a Redis Pub/Sub requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Attach the Redis adapter to Socket.IO
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;socket.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@socket.io/redis-adapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subClient&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Emit events — nothing changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No changes to your business logic. Redis handles the cross-pod sync entirely behind the scenes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;After the fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Events are synchronized across all pods&lt;/li&gt;
&lt;li&gt;Real-time communication works reliably in Kubernetes&lt;/li&gt;
&lt;li&gt;No more silent "missing messages"&lt;/li&gt;
&lt;li&gt;The fix required &lt;strong&gt;zero changes&lt;/strong&gt; to business logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Things You Should Know Before Using This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Redis is now a critical dependency
&lt;/h3&gt;

&lt;p&gt;If Redis goes down → your real-time layer breaks. Plan for Redis high-availability (Redis Sentinel or Redis Cluster) in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Latency increases slightly
&lt;/h3&gt;

&lt;p&gt;There's a small overhead introduced by the pub/sub round-trip. For most real-time apps, this is negligible — but worth knowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sticky sessions are no longer required
&lt;/h3&gt;

&lt;p&gt;One of the nice side effects: Redis removes the need for session affinity (sticky sessions) at the load balancer. Any user can hit any pod.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alternative Approaches — And Why We Didn't Use Them
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sticky Sessions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routes each user to the same pod permanently&lt;/li&gt;
&lt;li&gt;Breaks horizontal scalability — defeats the purpose of multiple pods&lt;/li&gt;
&lt;li&gt;A temporary band-aid, not a real fix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kafka / RabbitMQ&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powerful, but significant operational overhead&lt;/li&gt;
&lt;li&gt;Overkill for a straightforward real-time sync requirement&lt;/li&gt;
&lt;li&gt;Redis Pub/Sub hits the sweet spot: simple, fast, battle-tested&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;If you're scaling a real-time system horizontally:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In-memory sockets won't scale across pods. You need a shared messaging layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Redis Pub/Sub is one of the simplest and most effective ways to bridge that gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;This wasn't a bug — it was an &lt;strong&gt;architectural gap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once you understand that each pod is an isolated process with no awareness of other pods, you start designing distributed systems differently. You stop assuming "in-process = reliable" and start asking "where's the shared state?"&lt;/p&gt;

&lt;p&gt;And that's when things actually scale.&lt;/p&gt;

</description>
      <category>redis</category>
      <category>kubernetes</category>
      <category>node</category>
      <category>websockets</category>
    </item>
  </channel>
</rss>
