DEV Community

Cover image for Building Real-time Apps with Rails 8, Hotwire & ActionCable in Production
Chandra Shettigar
Chandra Shettigar

Posted on

5 2 1 1

Building Real-time Apps with Rails 8, Hotwire & ActionCable in Production

Rails 8 brings big improvements to real-time updates with Hotwire and ActionCable. Locally, everything works smoothly—your browser talks directly to the Rails app, and WebSockets handle real-time updates instantly. But in production, things are different:

  • Web traffic passes through load balancers and Kubernetes (or other orchestration tools).
  • Jobs run in separate containers, not inside your Rails app.
  • Redis is no longer required for ActionCable — it can now use your database (MySQL/PostgreSQL).

Let's compare Rails 8 real-time setup in local vs. production and walk through the key changes you need to make.


1. How Rails 8 Handles Real-time Updates Locally

In local development, everything is simple:

  • Your browser connects directly to Rails (Puma server).
  • WebSockets are handled inside Puma (/cable endpoint).
  • Jobs run inside Puma (if using Async adapter).

Flow in Local Setup:

Browser → Puma (Rails) → WebSockets (ActionCable)
          ↑            ↓
         Jobs (ActiveJob, Sidekiq, etc.)
Enter fullscreen mode Exit fullscreen mode

How it Works in Local:

  1. The browser opens a WebSocket connection to /cable.
  2. Turbo Streams subscribe to updates using turbo_stream_from.
  3. When a new message is created in Rails, it:
    • Calls broadcast_append_to, which sends the update over WebSockets.
    • The UI updates instantly, no page reload needed.

Easy setup—no external dependencies like Redis or a separate database for WebSockets.


2. How Rails 8 Handles Real-time Updates in Production

Once you deploy to AWS + Kubernetes, things change:

  • Browsers don’t directly talk to Puma. Instead, traffic goes through a load balancer (AWS ALB, NGINX Ingress, etc.).
  • Jobs run in separate pods (like Sidekiq workers).
  • WebSockets need to work across multiple app instances.
  • Redis is no longer required — Rails 8 supports database-backed pub/sub for ActionCable.

Flow in Production Setup:

Browser → AWS Load Balancer → Kubernetes Ingress → Rails Pod (Puma)
                                          |
                                          ├→ Sidekiq Pod (Runs Jobs)
                                          ├→ Database (Stores ActionCable Messages)

Enter fullscreen mode Exit fullscreen mode

How it Works in Production:

  1. The browser opens a WebSocket connection via the load balancer and ingress controller.
  2. ActionCable listens for changes in either Redis or the database (Rails 8 default).
  3. When a job (e.g., a new chat message) runs in a separate container, it:
    • Inserts a message into the database (if using DB pub/sub).
    • OR Publishes to Redis (if using Redis pub/sub).
  4. ActionCable picks up the new message and sends it to all subscribed clients via WebSockets.
  • Database-backed ActionCable (Rails 8) reduces dependency on Redis.
  • Jobs can now update WebSockets even if they run in separate pods.

3. Setting Up WebSockets in Production

Step 1: Configure ActionCable to Use the Database

In Rails 8, you can now store WebSocket updates in MySQL or PostgreSQL instead of Redis.

Edit config/cable.yml:

production:
  adapter: postgresql
  url: postgresql://prod-db-host/myapp_prod
Enter fullscreen mode Exit fullscreen mode

Or Mysql

production:
  adapter: mysql2
  url: mysql2://prod-db-host/myapp_prod
Enter fullscreen mode Exit fullscreen mode

Now, ActionCable will store real-time updates in the database instead of Redis.

Step 2: Create the ActionCable Database Table

Run the migration:

bin/rails action_cable:install
bin/rails db:migrate
Enter fullscreen mode Exit fullscreen mode

This creates a pub/sub table for WebSocket messages.


4. Handling WebSockets with Load Balancers & Kubernetes

AWS Load Balancer: Enable WebSockets

If you're using AWS ALB, make sure WebSockets are enabled:

aws elbv2 modify-listener --load-balancer-arn YOUR_LB_ARN --protocol HTTP2
Enter fullscreen mode Exit fullscreen mode

Kubernetes Ingress: Allow WebSocket Connections

If using NGINX Ingress, add WebSocket support in ingress.yaml:

nginx.ingress.kubernetes.io/websocket-services: "rails-app"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
Enter fullscreen mode Exit fullscreen mode

Now, WebSocket connections won’t be blocked by the load balancer.


5. Updating Real-time Messages in Production

Using Turbo Streams in Rails 8

In your Message model, automatically broadcast updates:

class Message < ApplicationRecord
  after_create_commit do
    broadcast_append_to "chat_room_#{chat_room_id}", target: "messages"
  end
end
Enter fullscreen mode Exit fullscreen mode

This will work locally and in production, regardless of whether you use Redis or the database.


6. Stimulus for Real-time UI Updates

We can use Stimulus to auto-scroll chat messages when new ones arrive.

Create chat_controller.js:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.addEventListener("turbo:before-stream-render", this.newMessage.bind(this));
  }

  newMessage() {
    this.element.scrollTop = this.element.scrollHeight;
  }
}
Enter fullscreen mode Exit fullscreen mode

Attach It to the Messages Box

<div id="messages" data-controller="chat">
  <%= turbo_stream_from "chat_room_#{chat_room.id}" %>
  <%= render @messages %>
</div>
Enter fullscreen mode Exit fullscreen mode

Now, new messages trigger turbo:before-stream-render, scrolling the chat automatically.


7. Summary: Rails 8 Local vs. Production

Feature Local (Puma Only) Production (AWS + K8s)
WebSockets Handled by Puma Needs ALB + Ingress Config
Job Execution Inside Puma Runs in Sidekiq Pod
Broadcast Mechanism Direct Turbo Stream DB Pub/Sub OR Redis
Scaling Works out of the box Requires WebSocket Load Balancing

8. Final Thoughts

With Rails 8, real-time updates are easier than ever: No more Redis dependency—ActionCable can now use MySQL/PostgreSQL.

  • Works with Kubernetes and AWS Load Balancers.
  • Turbo Streams + Stimulus keep things simple.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay