Desarrollo backend

Laravel Reverb en 2026: servidor WebSocket en PHP sin Node.js ni servicios externos

Ruslan Ismailov Publicado 12 min de lectura
L

Introducción: el problema del tiempo real en aplicaciones PHP

Durante mucho tiempo, PHP fue considerado un lenguaje de "petición-respuesta": llega una solicitud HTTP, se devuelve una respuesta. Implementar funcionalidades en tiempo real significaba añadir Node.js al stack, pagar por Pusher o Ably, o lidiar con Soketi autohospedado. Cada uno de estos caminos añadía complejidad: otro runtime, un nuevo servicio, costes adicionales o un mantenimiento propio inestable.

La historia de las soluciones disponibles era más o menos así:

  • Pusher — cómodo, pero de pago y los datos van a un servidor externo.
  • Socket.io + Node.js — potente, pero implica tener dos runtimes en el proyecto.
  • Soketi — servidor compatible con Pusher autohospedado en Node.js. Mejor opción, pero sigue siendo Node.
  • Ably — solución cloud con un plan gratuito más generoso, pero con la misma dependencia de terceros.

En marzo de 2024, el equipo de Laravel presentó Laravel Reverb — el primer servidor WebSocket oficial, escrito en PHP puro e integrado directamente en el ecosistema del framework. En 2026, ya es una solución madura y lista para producción. Veamos cómo funciona y cómo utilizarlo.

Qué es Laravel Reverb y cómo surgió

Laravel Reverb es un servidor WebSocket de primera clase para Laravel, implementado sobre la librería ReactPHP. Es totalmente compatible con el protocolo Pusher Channels, lo que significa que el código frontend que funcionaba con Pusher o Soketi puede migrar a Reverb sin cambios: solo hay que actualizar las credenciales en la configuración.

Reverb nació como respuesta a una de las peticiones más populares de la comunidad: permitir a los desarrolladores de Laravel crear aplicaciones en tiempo real sin salir del ecosistema PHP. Joe Dixon y el equipo de Laravel dedicaron varios meses a construir un event loop asíncrono sobre ReactPHP, que permite a PHP mantener miles de conexiones WebSocket abiertas dentro de un único proceso de larga duración.

«Reverb está diseñado para que los desarrolladores de Laravel puedan construir aplicaciones en tiempo real con la misma facilidad con la que crean endpoints HTTP convencionales.» — equipo de Laravel

Arquitectura: cómo funciona Reverb por dentro

Entender la arquitectura de Reverb es fundamental para configurarlo correctamente y no llevarse sorpresas en producción.

Event Loop y ReactPHP

Reverb se ejecuta como un proceso PHP de larga duración (no muere tras cada petición). Internamente usa un event loop basado en ReactPHP, que acepta conexiones WebSocket entrantes y procesa los mensajes de forma asíncrona. Esto es fundamentalmente diferente del modelo estándar de PHP-FPM.

Integración con Broadcasting y Event

Laravel Broadcasting es una abstracción sobre el transporte de eventos. Cuando se llama a broadcast(new OrderShipped($order)), Laravel Broadcasting decide a dónde enviar el evento: a Pusher, a Redis, a Reverb u otro destino. Reverb se registra como driver de broadcasting con el nombre reverb.

La cadena de funcionamiento es la siguiente:

  1. El código PHP dispara un evento mediante broadcast().
  2. Laravel Broadcasting lo envía a la cola (o directamente) al servidor Reverb por HTTP/WebSocket.
  3. Reverb distribuye el mensaje a todos los clientes suscritos en los canales correspondientes.
  4. El frontend recibe el evento a través de laravel-echo y el SDK de Pusher JS.

Instalación y configuración básica de Reverb

Veamos la instalación paso a paso. Se asume que ya tienes un proyecto Laravel versión 11 o superior.

Instalación del paquete

composer require laravel/reverb
php artisan reverb:install

El comando reverb:install publicará el archivo de configuración config/reverb.php, añadirá las variables necesarias al .env y configurará Broadcasting con el driver reverb.

Configuración del .env

BROADCAST_DRIVER=reverb

REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=0.0.0.0
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="localhost"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

Arranque del servidor

php artisan reverb:start
# o especificando host y puerto
php artisan reverb:start --host=0.0.0.0 --port=8080

Configuración del frontend

Instala las dependencias:

npm install --save-dev laravel-echo pusher-js

Configura Echo en resources/js/bootstrap.js:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT,
    wssPort: import.meta.env.VITE_REVERB_PORT,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

Escalado de Reverb con Redis

Una sola instancia de Reverb es perfecta para empezar, pero ¿qué ocurre cuando la carga crece y se necesitan varios servidores? Aquí es donde entra Redis.

Reverb soporta Redis como backend de pub/sub. Cuando se publica un evento en una instancia de Reverb, Redis lo distribuye al resto de instancias, que lo entregan a sus clientes conectados. Es el esquema clásico de escalado horizontal.

Configuración del backend Redis

# .env
REVERB_SCALING_ENABLED=true
REVERB_SCALING_DRIVER=redis

REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

En config/reverb.php, asegúrate de que la sección scaling tenga este aspecto:

'scaling' => [
    'enabled' => env('REVERB_SCALING_ENABLED', false),
    'channel' => 'reverb',
    'server' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REVERB_SCALING_REDIS_DATABASE', '0'),
    ],
],

Ahora puedes ejecutar múltiples instancias de php artisan reverb:start detrás de un balanceador de carga, y Redis sincronizará el estado entre todas ellas.

Despliegue de Reverb en Docker y Kubernetes

Configuración con Docker

Ejemplo de Dockerfile para una aplicación Laravel con Reverb:

FROM php:8.3-cli-alpine

RUN apk add --no-cache \
    git curl unzip libzip-dev \
    && docker-php-ext-install zip pcntl sockets

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app
COPY . .

RUN composer install --no-dev --optimize-autoloader

EXPOSE 8080

CMD ["php", "artisan", "reverb:start", "--host=0.0.0.0", "--port=8080"]

Ejemplo de docker-compose.yml:

version: '3.9'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - APP_ENV=production
      - REVERB_APP_ID=my-app-id
      - REVERB_APP_KEY=my-app-key
      - REVERB_APP_SECRET=my-app-secret
      - REVERB_SCALING_ENABLED=true
      - REDIS_HOST=redis
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

Kubernetes: Deployment y Service

En Kubernetes, el servidor Reverb se despliega como un Deployment independiente con varias réplicas. El tráfico WebSocket se expone mediante un Service de tipo LoadBalancer o a través de un Ingress con soporte para WebSocket.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reverb
spec:
  replicas: 3
  selector:
    matchLabels:
      app: reverb
  template:
    metadata:
      labels:
        app: reverb
    spec:
      containers:
        - name: reverb
          image: your-registry/laravel-app:latest
          command: ["php", "artisan", "reverb:start", "--host=0.0.0.0", "--port=8080"]
          ports:
            - containerPort: 8080
          env:
            - name: REVERB_SCALING_ENABLED
              value: "true"
            - name: REDIS_HOST
              value: redis-service
---
apiVersion: v1
kind: Service
metadata:
  name: reverb-service
spec:
  selector:
    app: reverb
  ports:
    - port: 8080
      targetPort: 8080
  type: LoadBalancer

Importante: asegúrate de que tu controlador Ingress (por ejemplo, nginx-ingress) esté configurado para admitir la actualización de conexiones a WebSocket mediante las cabeceras Upgrade y Connection.

Ejemplo práctico: chat en tiempo real

Crearemos un chat público sencillo usando Laravel Broadcasting y Vue 3.

Evento en el servidor

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;

class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets;

    public function __construct(
        public string $message,
        public string $username
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel('public-chat');
    }

    public function broadcastAs(): string
    {
        return 'message.sent';
    }
}

Controlador

<?php

namespace App\Http\Controllers;

use App\Events\MessageSent;
use Illuminate\Http\Request;

class ChatController extends Controller
{
    public function send(Request $request)
    {
        $request->validate(['message' => 'required|string|max:500']);

        broadcast(new MessageSent(
            message: $request->message,
            username: $request->user()->name ?? 'Anonymous'
        ));

        return response()->json(['status' => 'sent']);
    }
}

Componente Vue

<script setup>
import { ref, onMounted } from 'vue';

const messages = ref([]);
const newMessage = ref('');

onMounted(() => {
    window.Echo.channel('public-chat')
        .listen('.message.sent', (event) => {
            messages.value.push(event);
        });
});

const sendMessage = async () => {
    await fetch('/chat/send', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content },
        body: JSON.stringify({ message: newMessage.value })
    });
    newMessage.value = '';
};
</script>

<template>
  <div>
    <div v-for="msg in messages" :key="msg.message">
      <strong>{{ msg.username }}:</strong> {{ msg.message }}
    </div>
    <input v-model="newMessage" @keyup.enter="sendMessage" placeholder="Escribe un mensaje..." />
  </div>
</template>

Comparativa: Reverb vs Soketi vs Pusher en 2026

La elección de la herramienta depende de tus necesidades. Así está el panorama en 2026:

  • Laravel Reverb — PHP nativo, sin runtime externo, gratuito, excelente integración con Laravel Broadcasting y soporte de Redis para escalar. Ideal para equipos que trabajan en el stack PHP. En 2026 es estable y está mantenido por el equipo de Laravel.
  • Soketi — autohospedado, Node.js, compatible con Pusher. Funciona bien, pero requiere Node.js. Su desarrollo se ralentizó tras la llegada de Reverb y parte de la comunidad ha migrado.
  • Pusher — cloud, cero mantenimiento, plan gratuito generoso (200 conexiones simultáneas). La opción para startups y MVPs donde no se quiere gestionar infraestructura. El inconveniente: los datos van a un servidor externo, hay límites y el coste escala con el crecimiento.
  • Ably — servicio cloud más avanzado con garantías de entrega, historial de mensajes y red global. Más caro que Pusher al escalar, pero con más funcionalidades.

Conclusión: si trabajas en el stack de Laravel y quieres control total sobre la infraestructura, Reverb es tu elección en 2026.

Limitaciones y cuándo elegir otra herramienta

Reverb es una herramienta excelente, pero tiene limitaciones que conviene conocer:

  • Un hilo por proceso. PHP no es Go ni Erlang. A pesar del event loop, Reverb funciona en un único hilo. Para cargas extremas (cientos de miles de conexiones simultáneas) será necesaria una cuidadosa configuración del escalado horizontal con Redis.
  • Sin historial de mensajes integrado. Reverb no almacena historial. Si se necesita la entrega de mensajes perdidos, hay que implementarlo manualmente mediante base de datos o elegir Ably.
  • Los canales presence no persisten sin Redis. Los canales presence están soportados, pero su estado no se conserva entre reinicios del servidor si no se usa Redis.
  • Solo WebSocket. Reverb no soporta SSE (Server-Sent Events) ni Long Polling como fallback. Si se necesita compatibilidad con entornos sin WebSocket, Laravel Echo con otro transporte o Mercure son mejores opciones.
  • La carga en producción requiere un supervisor. Reverb debe ejecutarse bajo Supervisor u otro gestor de procesos para que se reinicie automáticamente en caso de fallo.

Conclusión

Laravel Reverb en 2026 es una solución madura y lista para producción para el tiempo real en aplicaciones PHP. Elimina la principal barrera: ya no se necesita Node.js, ni un servicio externo, ni gastar dinero en Pusher. Escribes PHP y Reverb entrega los eventos a los clientes por WebSocket.

Puedes empezar en 15 minutos: instala el paquete, añade las variables al .env, ejecuta php artisan reverb:start y el tiempo real en tu proyecto Laravel ya está funcionando. Después puedes añadir Redis para escalar, y Docker o Kubernetes para un despliegue robusto.

Experimenta, construye chats, dashboards en tiempo real, notificaciones en vivo: la herramienta está lista. El stack se mantiene en PHP puro, el equipo de Laravel da soporte a Reverb y lo sigue desarrollando. Es el momento ideal para probarlo.

Tecnologías

Etiquetas

Ruslan Ismailov

Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →