Laravel Reverb adalah server WebSocket untuk ekosistem Laravel yang dirancang agar integrasi real-time terasa natural dengan sistem broadcasting bawaan framework. Di lingkungan production, Reverb bukan sekadar menjalankan proses tambahan, tetapi bagian dari arsitektur yang harus dipikirkan dengan benar: bagaimana koneksi masuk, bagaimana event dikirim, bagaimana autentikasi channel dilakukan, bagaimana proses dijaga tetap hidup, dan bagaimana sistem dipantau ketika trafik meningkat.
Artikel ini membahas setup Laravel Reverb di production secara praktis. Fokusnya bukan hanya agar "bisa jalan", tetapi agar rapi, aman, mudah dioperasikan, dan cukup stabil untuk kebutuhan nyata seperti notifikasi, chat, dashboard monitoring, atau update status pekerjaan secara langsung.
Memahami Arsitektur Laravel Reverb di Production
Sebelum masuk ke konfigurasi, penting memahami alur dasarnya. Dalam arsitektur Laravel dengan Reverb, biasanya ada beberapa komponen berikut:
- Aplikasi Laravel yang menerima request HTTP biasa, menjalankan logika bisnis, lalu melempar event broadcast.
- Queue worker yang memproses event broadcast secara asinkron jika event di-queue.
- Server Reverb yang menerima event broadcast dari Laravel lalu meneruskannya ke client WebSocket yang berlangganan channel terkait.
- Client frontend yang memakai Laravel Echo atau client kompatibel Pusher protocol untuk membuka koneksi WebSocket.
- Reverse proxy seperti Nginx atau Caddy yang menangani TLS/SSL, routing domain, dan upgrade koneksi WebSocket.
Secara sederhana, alurnya seperti ini:
- Browser membuka koneksi WebSocket ke domain Reverb.
- User melakukan subscribe ke channel tertentu.
- Aplikasi Laravel memicu event yang mengimplementasikan
ShouldBroadcast. - Event diproses, biasanya melalui queue.
- Reverb menerima payload event dan mendorongnya ke client yang sedang subscribe.
Pola ini bekerja baik karena WebSocket connection bersifat persisten, sementara event backend tetap mengikuti alur Laravel yang familiar. Namun di production, stabilitas sangat ditentukan oleh reverse proxy, queue yang sehat, konfigurasi host/port yang benar, dan pengelolaan proses server.
Kebutuhan Server dan Topologi yang Masuk Akal
Minimal kebutuhan operasional
Untuk deployment production yang rapi, Anda biasanya membutuhkan:
- Server Linux dengan PHP CLI yang sesuai dengan kebutuhan aplikasi Laravel.
- Ekstensi dan dependensi standar Laravel.
- Nginx atau reverse proxy sejenis.
- Supervisor atau systemd untuk menjaga proses Reverb dan queue worker tetap hidup.
- Redis sangat disarankan untuk queue, cache, dan kadang membantu koordinasi performa aplikasi.
- SSL/TLS aktif di level reverse proxy.
Untuk aplikasi kecil, Reverb bisa berjalan di server yang sama dengan aplikasi Laravel. Untuk aplikasi yang mulai sibuk, Anda bisa memisahkan:
- App server untuk HTTP request dan API
- Worker server untuk queue
- WebSocket server untuk Reverb
Pemisahan ini tidak wajib sejak awal, tetapi membantu ketika koneksi persisten mulai memakan memori dan Anda ingin isolasi beban kerja.
Port dan jaringan
Umumnya Reverb berjalan pada port internal, misalnya 8080 atau 6001, lalu diekspos ke publik melalui Nginx di port 443. Ini lebih aman dan lebih mudah dikelola dibanding membuka port WebSocket langsung ke internet.
Praktik yang disarankan: biarkan Reverb hanya mendengarkan di
127.0.0.1atau private interface, lalu gunakan reverse proxy sebagai pintu masuk publik.
Instalasi dan Konfigurasi Broadcasting
Menyiapkan Reverb
Setelah Laravel Reverb terpasang di aplikasi, langkah penting berikutnya adalah memastikan driver broadcasting menggunakan Reverb dan nilai environment konsisten antara backend dan frontend.
Contoh konfigurasi environment yang umum:
BROADCAST_CONNECTION=reverb
QUEUE_CONNECTION=redis
REVERB_APP_ID=myapp
REVERB_APP_KEY=base64-or-random-key
REVERB_APP_SECRET=very-secret-value
REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080Perlu dibedakan dua hal berikut:
- Host/port server Reverb: tempat proses Reverb mendengarkan koneksi.
- Host/port yang dipakai aplikasi/client: alamat yang digunakan untuk mengakses Reverb, sering kali lewat domain publik dan reverse proxy.
Kesalahan umum di production adalah mencampur alamat internal dan alamat publik, sehingga backend bisa mengirim event tetapi browser gagal terkoneksi, atau sebaliknya.
Konfigurasi broadcasting Laravel
Pastikan file konfigurasi broadcasting menunjuk ke koneksi Reverb. Pada praktiknya, Laravel akan memakai kredensial REVERB_APP_ID, REVERB_APP_KEY, dan REVERB_APP_SECRET untuk menandatangani permintaan yang relevan. Jangan menaruh nilai ini sembarangan di frontend selain key publik yang memang dibutuhkan client.
Jika Anda mengubah environment di production, jangan lupa:
php artisan config:clear
php artisan cache:clear
php artisan config:cacheSering kali masalah broadcast ternyata bukan bug Reverb, melainkan konfigurasi lama yang masih tersimpan di cache.
Menjalankan server Reverb
Saat pengembangan Anda mungkin menjalankannya manual. Di production, gunakan process manager seperti Supervisor atau systemd.
Contoh unit systemd sederhana:
[Unit]
Description=Laravel Reverb
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php artisan reverb:start --host=0.0.0.0 --port=8080
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetGunakan pendekatan serupa untuk queue worker. Reverb tanpa queue worker yang sehat akan terlihat seperti "kadang jalan, kadang tidak", terutama jika event broadcast diantrekan.
Event, Queue, dan Pola Pengiriman yang Stabil
Contoh event broadcast
Berikut contoh event yang realistis untuk update status pesanan:
<?php
namespace App\Events;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcast
{
use Dispatchable, SerializesModels;
public function __construct(
public int $orderId,
public string $status,
public string $updatedAt,
) {}
public function broadcastOn(): array
{
return [new PrivateChannel('orders.' . $this->orderId)];
}
public function broadcastAs(): string
{
return 'order.status.updated';
}
public function broadcastWith(): array
{
return [
'order_id' => $this->orderId,
'status' => $this->status,
'updated_at' => $this->updatedAt,
];
}
}Mengapa pola ini baik untuk production?
- Payload ringkas, tidak mengirim seluruh model.
- Channel spesifik sehingga client hanya menerima data yang relevan.
- Nama event eksplisit dan mudah dilacak di frontend.
Kapan memakai queue?
Dalam production, event broadcast sebaiknya diproses lewat queue untuk menghindari latensi tambahan pada request utama. Misalnya, ketika user mengubah status pesanan, response HTTP tidak perlu menunggu proses broadcast selesai.
Keuntungannya:
- Response API lebih cepat.
- Lonjakan event lebih mudah ditangani.
- Kegagalan sementara bisa di-retry oleh worker.
Trade-off-nya adalah Anda menambah komponen operasional: queue backend, worker, retry policy, dan monitoring job gagal.
Listener dan pemisahan tanggung jawab
Tidak semua notifikasi real-time harus dilakukan langsung dari controller. Anda bisa memakai event domain internal, lalu listener memicu event broadcast yang memang ditujukan ke client.
<?php
namespace App\Listeners;
use App\Events\OrderStatusUpdated;
use App\Events\OrderWasProcessed;
class BroadcastProcessedOrder
{
public function handle(OrderWasProcessed $event): void
{
broadcast(new OrderStatusUpdated(
orderId: $event->order->id,
status: $event->order->status,
updatedAt: now()->toIso8601String(),
));
}
}Pemisahan ini berguna jika Anda ingin menjaga event domain tetap bersih, sementara kebutuhan real-time bisa berubah tanpa mengganggu logika inti aplikasi.
Autentikasi Channel dan Keamanan
Private dan presence channel
Untuk data sensitif, gunakan private channel atau presence channel. Jangan mengirim data pelanggan, transaksi, atau informasi internal melalui public channel.
Definisikan otorisasi channel di file channel routes:
<?php
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('orders.{orderId}', function ($user, int $orderId) {
return $user->orders()->whereKey($orderId)->exists();
});Mengapa ini penting? Karena subscribe ke private channel memicu request autentikasi ke backend. Di sanalah Laravel memutuskan apakah user boleh mendengarkan channel tertentu. Jika aturan ini salah, user bisa menerima event milik orang lain.
Hal yang perlu diperhatikan
- Pastikan guard autentikasi sesuai, terutama jika frontend memakai session cookie atau token API.
- Jika menggunakan SPA lintas subdomain, periksa konfigurasi cookie, same-site, dan CORS.
- Hindari query berat di callback channel auth karena bisa dipanggil cukup sering.
Konfigurasi Client dengan Laravel Echo
Client biasanya menggunakan Laravel Echo dengan transport kompatibel Pusher protocol. Yang terpenting adalah menyamakan host, scheme, port, dan endpoint auth dengan kondisi production.
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: Number(import.meta.env.VITE_REVERB_PORT || 80),
wssPort: Number(import.meta.env.VITE_REVERB_PORT || 443),
forceTLS: import.meta.env.VITE_REVERB_SCHEME === 'https',
enabledTransports: ['ws', 'wss'],
authEndpoint: '/broadcasting/auth',
withCredentials: true,
});
window.Echo.private('orders.123')
.listen('.order.status.updated', (event) => {
console.log('Order updated', event);
});Jika aplikasi berada di balik reverse proxy dan SSL aktif, client biasanya harus memakai wss melalui domain publik, bukan port internal Reverb. Ini sumber error yang sangat umum.
Reverse Proxy, SSL, dan Routing yang Benar
Nginx untuk WebSocket
Reverse proxy harus meneruskan header upgrade dengan benar. Contoh konfigurasi Nginx dasar:
server {
listen 443 ssl http2;
server_name ws.example.com;
ssl_certificate /etc/letsencrypt/live/ws.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ws.example.com/privkey.pem;
location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://127.0.0.1:8080;
}
}Intinya:
UpgradedanConnectionwajib benar agar handshake WebSocket berhasil.proxy_read_timeoutjangan terlalu kecil, karena koneksi WebSocket memang idle dalam waktu lama.- TLS sebaiknya dihentikan di Nginx, lalu Reverb berjalan di HTTP internal.
Domain terpisah atau satu domain?
Dua pendekatan yang umum:
- Subdomain khusus, misalnya
ws.example.com. Lebih mudah dipisahkan dan diobservasi. - Satu domain utama dengan path tertentu. Bisa lebih sederhana untuk beberapa deployment, tetapi routing dan CORS harus lebih hati-hati.
Untuk production, subdomain khusus sering lebih rapi karena lalu lintas WebSocket dan HTTP biasa tidak tercampur terlalu banyak.
Scaling Dasar dan Batasan yang Perlu Dipahami
WebSocket berbeda dari request-response HTTP biasa karena koneksi cenderung bertahan lama. Artinya, scaling dipengaruhi oleh jumlah koneksi simultan, memori, dan kemampuan server menjaga socket tetap aktif.
Kapan satu instance cukup?
Jika aplikasi masih moderat, satu instance Reverb di belakang Nginx biasanya cukup. Fokuskan dulu pada:
- Queue worker stabil
- Redis sehat
- Memory limit dan file descriptor server memadai
- Monitoring koneksi dan error
Kapan perlu lebih dari satu instance?
Jika koneksi aktif tinggi atau Anda ingin high availability, Anda bisa menjalankan beberapa instance Reverb di belakang load balancer. Namun, scaling WebSocket tidak sesederhana menambah pod atau VM saja. Anda harus memastikan strategi routing dan koordinasi event antar node sesuai dengan kebutuhan aplikasi.
Pada tahap awal, solusi praktis sering kali adalah:
- pisahkan Reverb dari app server,
- gunakan Redis untuk komponen pendukung Laravel,
- naikkan jumlah worker queue,
- pastikan load balancer mendukung WebSocket dengan benar.
Jangan terburu-buru melakukan autoscaling agresif tanpa observability. Banyak masalah yang terlihat seperti kebutuhan scale ternyata hanya salah konfigurasi timeout, auth, atau reverse proxy.
Masalah Umum di Production dan Cara Debug
Koneksi sering putus
Penyebab umum:
proxy_read_timeoutterlalu kecil- Load balancer menutup koneksi idle
- SSL termination bermasalah
- Server kehabisan resource
Langkah cek:
- Lihat log Nginx dan log aplikasi
- Pastikan browser benar-benar memakai
wss://jika situs berjalan di HTTPS - Periksa apakah restart proses Reverb terlalu sering
Port salah atau tidak bisa diakses
Kasus klasik: backend memakai 127.0.0.1:8080, tapi frontend mencoba konek langsung ke port itu dari browser. Ini tidak akan bekerja jika port tidak diekspos publik. Browser harus diarahkan ke domain publik reverse proxy, misalnya ws.example.com:443.
CORS dan auth channel gagal
Jika private channel gagal subscribe, jangan langsung menyalahkan Reverb. Sering kali masalahnya ada di endpoint /broadcasting/auth:
- cookie session tidak terkirim,
- header CSRF/token tidak sesuai,
- origin frontend belum diizinkan,
- guard auth salah.
Debug dengan membuka tab Network di browser dan periksa response endpoint auth. Jika status 403 atau 401, masalahnya ada pada autentikasi/otorisasi, bukan pada socket transport.
Event tidak sampai meskipun koneksi tersambung
Beberapa kemungkinan:
- Queue worker tidak berjalan
- Event masuk ke queue yang salah
- Nama channel atau nama event tidak cocok dengan frontend
- Konfigurasi cache belum dibersihkan setelah perubahan env
Untuk verifikasi, coba gunakan log sederhana saat event didispatch, saat job diproses, dan saat client menerima event. Dengan begitu Anda bisa tahu putusnya di mana: di aplikasi, queue, Reverb, atau client.
Observability: Jangan Menunggu Error Besar Baru Mencari Tahu
Di production, WebSocket perlu dipantau seperti layanan penting lain. Minimal, siapkan:
- Log proses Reverb untuk error startup, bind port, atau koneksi gagal.
- Log queue worker untuk job broadcast yang gagal.
- Metrics dasar seperti penggunaan CPU, memori, restart process, dan jumlah koneksi aktif jika tersedia.
- Health check untuk memastikan reverse proxy dan proses Reverb benar-benar berjalan.
Jika Anda memakai systemd atau Supervisor, pastikan log tidak hilang begitu saja. Kirim ke sistem log terpusat bila perlu. Untuk incident nyata, informasi seperti waktu restart proses, lonjakan koneksi, dan error auth channel sangat membantu mempercepat diagnosis.
Prinsip praktis: anggap Reverb sebagai service terpisah, bukan sekadar fitur tambahan Laravel. Dengan begitu deployment, logging, restart policy, dan monitoring akan lebih disiplin.
Rekomendasi Implementasi yang Aman untuk Memulai
Jika Anda baru membawa Reverb ke production, pendekatan berikut cukup aman:
- Jalankan Reverb di port internal, misalnya
127.0.0.1:8080. - Letakkan Nginx di depan dengan SSL aktif dan header upgrade yang benar.
- Gunakan Redis untuk queue dan cache.
- Proses event broadcast melalui queue worker.
- Pakai private channel untuk data user-spesifik.
- Siapkan systemd atau Supervisor untuk Reverb dan worker.
- Aktifkan logging yang cukup untuk auth, queue, dan restart process.
Dengan fondasi ini, Anda sudah memiliki setup yang layak untuk sebagian besar kebutuhan real-time skala kecil hingga menengah.
Penutup
Laravel Reverb memberi cara yang lebih natural untuk membangun fitur real-time di ekosistem Laravel, tetapi keberhasilan di production bergantung pada disiplin operasional. Kunci utamanya adalah memahami arsitektur koneksi persisten, memisahkan konfigurasi internal dan publik, menjaga queue tetap sehat, mengamankan private channel, dan memastikan reverse proxy menangani WebSocket dengan benar.
Mulailah dari topologi yang sederhana namun rapi, lalu tambahkan scaling saat memang dibutuhkan. Jika observability sudah baik sejak awal, Anda akan jauh lebih mudah membedakan apakah masalah berasal dari aplikasi, queue, proxy, atau jaringan.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!