Solid Cable: WebSockets and Real-Time Without Redis


Welcome to the second article in our series on the Solid Triumvirate in Rails 8! Today, we’re tackling a hot topic if you want to build a Rails app that lives in the present: real-time.


Solid Cable is the new way to integrate WebSockets in Rails without wrestling with Redis or external servers. It’s native, it’s simple, and it’s powerful.


But why is real-time so important today? Because your users expect it. Here are some concrete use cases where Solid Cable shines:

  1. 💬 A live chat between users, without reloading the page
  2. 📈 A dashboard that updates automatically when data changes
  3. 🔔 Instant notifications as soon as an event occurs
  4. 🛒 A shared cart that stays in sync live across multiple devices
  5. 🎮 A collaborative app where several people interact at the same time


With Solid Cable, all of this becomes possible without friction. In this article, we’ll show how to install it, configure it, and especially how to use it to create dynamic, modern experiences.


Feel free to browse my other articles on the Solid Triumvirate topic that will no longer hold any secrets for you!


  1. "Solid Cache" : No longer relying on Redis for cache in Rails
  2. "Solid Cable" : WebSockets and real-time simply and without fuss (you’re here)
  3. "Solid Queue" : Background tasks Rails-style
  4. The triumvirate applied to a complete project


This is going to be solid :)

Solid Cable: WebSockets Without Redis in Rails 8

Understanding Action Cable and Real-Time

Action Cable is Rails' solution for adding real-time features to your application. Think of:

  1. A live chat
  2. Instant notifications
  3. Dashboards with live data
  4. Real-time collaboration (like Google Docs)

How do WebSockets work?

WebSockets are like a phone that’s always off the hook between the client and the server:


Classical HTTP:

Client: "Bonjour serveur, quoi de neuf ?"
Serveur: "Rien de spécial. Au revoir."
[Connection fermée]
[10 secondes plus tard]
Client: "Bonjour serveur, quoi de neuf maintenant ?"
Serveur: "Toujours rien. Au revoir."


WebSocket:

Client: "Bonjour serveur, je reste en ligne"
Serveur: "OK, je te préviens s'il y a du nouveau"
[Connection reste ouverte]
Serveur: "Hey ! Nouveau message !"
Client: "Merci, je l'affiche tout de suite"

The Role of Solid Cable

Traditionally, Action Cable used Redis as the "central exchange" to route messages between the different processes of your application.

Solid Cable replaces Redis with a database table and a polling system (regular polling).

The Architecture of Solid Cable

Solid Cable creates a simple table:

# db/cable_schema.rb
create_table "solid_cable_messages", force: :cascade do |t|
t.binary "channel", null: false # Le canal de communication
t.binary "payload", null: false # Le message lui-même
t.datetime "created_at", null: false
t.bigint "channel_hash", null: false # Hash du canal pour recherche rapide
end


How it works:

  1. A client connects via WebSocket
  2. When a message needs to be sent, it’s written into this table
  3. A polling system checks regularly (default every 0.1 seconds) for new messages
  4. The messages are transmitted to the connected clients
  5. Old messages are automatically deleted after 1 day

Installation and Configuration

On Rails 8

As with Solid Cache, Solid Cable is enabled by default in production on Rails 8!

Check your config/cable.yml:


production:
adapter: solid_cable
connects_to:
database:
writing: cable
polling_interval: 0.1.seconds
message_retention: 1.day

Development configuration

By default, Rails 8 uses the async adapter in development. To use Solid Cable in dev:


# config/cable.yml
development:
adapter: solid_cable
connects_to:
database:
writing: cable
polling_interval: 0.1.seconds
message_retention: 1.day

Then configure your config/database.yml:


development:
primary:
<<: *default
database: mon_app_development
cable:
<<: *default
database: mon_app_cable_development

Complete Example: Real-Time Chat

Let’s build a simple chat application to illustrate Solid Cable.

Step 0: Create a new project

# Vérifiez que vous avez Rails 8
rails -v
> 8.0.X
# Créez un nouveau projet
rails new chat_app
cd chat_app


Configure cable.yml to use solid_cable instead of async in development.

# Async adapter only works within the same process, so for manually triggering cable updates from a console,
# and seeing results in the browser, you must do so from the web console (running inside the dev process),
# not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
# to make the web console appear.
development:
adapter: solid_cable
test:
adapter: test
production:
adapter: solid_cable
connects_to:
database:
writing: cable
polling_interval: 0.1.seconds
message_retention: 1.day

In development you need to add the solid_cable database schema to the database. In production, there will be a dedicated database so not needed.


bin/rails db:schema:load SCHEMA=db/cable_schema.rb


Step 1: Create the model

rails g model Message content:text
rails db:migrate

Step 2: Create the channel

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
# S'abonne au stream "chat"
stream_from "chat"
end
def unsubscribed
# Nettoyage si nécessaire
end
def speak(data)
# Méthode appelée quand un message est envoyé
message = Message.create!(
content: data['message']
)
end
end

Step 3: The model with broadcast

# app/models/message.rb
class Message < ApplicationRecord
# Après création, broadcaster le message à tous les clients connectés
after_create_commit do
broadcast_append_to(
"chat",
target: "messages",
partial: "messages/message",
locals: { message: self }
)
end
end

Step 4: The controller

# app/controllers/messages_controller.rb
class MessagesController < ApplicationController
def index
@messages = Message.order(created_at: :desc).limit(50)
end
def create
@message = Message.new(message_params)
if @message.save
# Le broadcast est géré automatiquement par le callback
head :ok
else
render json: { errors: @message.errors }, status: :unprocessable_entity
end
end
private
def message_params
params.require(:message).permit(:content)
end
end

Step 5: The view

<!-- app/views/messages/index.html.erb -->
<div class="chat-container">
<h1>Chat en Direct</h1>
<%= turbo_stream_from "chat" %>
<!-- Zone d'affichage des messages -->
<div id="messages" class="messages-list">
<%= render @messages %>
</div>
<!-- Formulaire d'envoi -->
<%= form_with(
model: Message.new,
id: "message-form"
) do |form| %>
<%= form.text_field :content,
placeholder: "Tapez votre message..." %>
<%= form.submit "Envoyer" %>
<% end %>
</div>



<!-- app/views/messages/_message.html.erb -->
<div class="message" id="<%= dom_id(message) %>">
<%= message.content %>
<span class="timestamp"><%= time_ago_in_words(message.created_at) %> ago</span>
</div>

Step 6: Routes

# config/routes.rb
Rails.application.routes.draw do
resources :messages, only: [:index, :create]
root "messages#index"
end

Testing in Action

  1. Start your server: bin/dev
  2. Open two browser windows at http://localhost:3000
  3. Type a message in one window
  4. See it appear instantly in the other!


This real-time chat relies on Turbo Streams and Solid Cable: the page subscribes to the stream with turbo_stream_from "chat". When a message is submitted via form_with, the controller creates a Message record.

After the commit, the model triggers broadcast_append_to "chat", and Solid Cable (the production adapter of ActionCable) persists and distributes this broadcast.

On the client side, @hotwired/turbo-rails automatically receives the stream and updates the DOM by adding the new message into #messages, with no custom JavaScript and no page reload.

Monitoring and Debugging

Verifying messages in the database

# Console Rails
SolidCable::Message.count
# Devrait être faible car les messages sont supprimés après 1 jour
SolidCable::Message.last
# Voir le dernier message broadcasté

Viewing logs

# Dans vos logs Rails, vous verrez :
SolidCable::Message Insert (0.5ms)
INSERT INTO "solid_cable_messages" ...
Turbo::StreamsChannel transmitting
"<turbo-stream action='append' target='messages'>..."

Solid Cable vs Other Solutions

Solution
Speed
Setup
Scalability
Use cases
Solid Cable
Good
Very simple
Medium
Small/medium apps
Action Cable (Redis)
Very good
Simple
Good
Medium apps
AnyCable
Excellent
Complex
Excellent
Large-scale apps


Use Solid Cable if:

  1. You’re starting a new Rails 8 app
  2. You need basic real-time features
  3. You want to avoid Redis
  4. You have fewer than 1000 concurrent connections

Switch to Action Cable + Redis if:

  1. You need better performance
  2. You already have Redis in your stack

Switch to AnyCable if:

  1. You have thousands of concurrent connections
  2. Latency is critical


Conclusion: Solid Cable, Real-Time Without Compromise

Solid Cable redefines how we think about real-time in Rails. No more complex setups with Redis or external servers: everything runs locally, with seamless integration and a familiar API. Whether it’s a chat, notifications, dashboards, or collaborative experiences, Solid Cable lets you build reactive, modern interfaces without sacrificing simplicity.

With Rails 8 officially adopting it, Solid Cable becomes an essential tool for any developer who wants to deliver a dynamic and instant user experience.


Fewer dependencies, more interactions. Solid Cable is Rails in real-time, as it should be.