<?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>Forem: Jayden Robbins</title>
    <description>The latest articles on Forem by Jayden Robbins (@darknautica).</description>
    <link>https://forem.com/darknautica</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%2F3875287%2F0a8c0911-a1e1-4f1f-8199-c681e0dad027.jpeg</url>
      <title>Forem: Jayden Robbins</title>
      <link>https://forem.com/darknautica</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/darknautica"/>
    <language>en</language>
    <item>
      <title>Relay vs Laravel Reverb: A Real Performance Benchmark</title>
      <dc:creator>Jayden Robbins</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:24:00 +0000</pubDate>
      <link>https://forem.com/darknautica/relay-vs-laravel-reverb-a-real-performance-benchmark-3md4</link>
      <guid>https://forem.com/darknautica/relay-vs-laravel-reverb-a-real-performance-benchmark-3md4</guid>
      <description>&lt;p&gt;We built Relay because we believed a WebSocket server written in Go would handle real-world connection loads more efficiently than one running in PHP. We wanted to find out by how much. So we ran a real benchmark. Here's exactly what we did, what we found, and what it means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark runner:&lt;/strong&gt; Hetzner CAX11 (4GB RAM, ARM64, Ubuntu 24.04)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relay server:&lt;/strong&gt; Hetzner CX23 — separate machine, tested over the network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverb server:&lt;/strong&gt; Running locally on the benchmark box (loopback — an advantage for Reverb)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load testing tool:&lt;/strong&gt; k6 v0.55.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test:&lt;/strong&gt; Ramp from 0 to 1,000 concurrent WebSocket connections over 5 minutes, hold at 1,000 for 60 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results at 1,000 Concurrent Connections
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Relay (Go)&lt;/th&gt;
&lt;th&gt;Laravel Reverb (PHP)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Process memory&lt;/td&gt;
&lt;td&gt;~38 MB&lt;/td&gt;
&lt;td&gt;~63 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU at peak&lt;/td&gt;
&lt;td&gt;~18%&lt;/td&gt;
&lt;td&gt;~95%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server load average&lt;/td&gt;
&lt;td&gt;0.62&lt;/td&gt;
&lt;td&gt;2.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total server RAM&lt;/td&gt;
&lt;td&gt;700 MB&lt;/td&gt;
&lt;td&gt;962 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the Numbers Mean
&lt;/h2&gt;

&lt;p&gt;Reverb hit 95% CPU. Relay hit 18%. That means Reverb is near its ceiling at 1,000 connections on a $5 server. Relay is barely warming up.&lt;/p&gt;

&lt;p&gt;The headroom difference is what matters in production. When traffic spikes, Relay absorbs it. Reverb starts dropping connections.&lt;/p&gt;

&lt;p&gt;Go's goroutine model is the reason. Each WebSocket connection in Relay is handled by a goroutine — lightweight, cheap to schedule, independently managed. PHP's event loop works, but it pays a higher price per connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Caveat
&lt;/h2&gt;

&lt;p&gt;Reverb ran on loopback while Relay ran over the network — a latency advantage for Reverb. Despite this, Relay won on every resource metric.&lt;/p&gt;

&lt;p&gt;Reverb is a well-built product with first-party Laravel support. If you're running a pure Laravel monolith and want Taylor Otwell's name behind your WebSocket server, Reverb is a solid choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Choose Relay
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Polyglot stack (Next.js, Rails, Django, Node — not just Laravel)&lt;/li&gt;
&lt;li&gt;You want a managed cloud option without vendor lock-in&lt;/li&gt;
&lt;li&gt;You want a live Channel Inspector in your dashboard&lt;/li&gt;
&lt;li&gt;Self-host free forever, optionally move to managed hosting with zero code changes&lt;/li&gt;
&lt;li&gt;Better resource efficiency at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Relay is open source (MIT) and free to self-host forever:&lt;br&gt;
👉 &lt;a href="https://github.com/DarkNautica/Relay" rel="noopener noreferrer"&gt;github.com/DarkNautica/Relay&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Relay Cloud is the optional managed tier with a free hobby plan:&lt;br&gt;
👉 &lt;a href="https://relaycloud.dev" rel="noopener noreferrer"&gt;relaycloud.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Laravel package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require darknautica/relay-cloud-laravel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://relaycloud.dev/blog/relay-vs-reverb-benchmark" rel="noopener noreferrer"&gt;relaycloud.dev/blog/relay-vs-reverb-benchmark&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>websockets</category>
      <category>go</category>
      <category>php</category>
    </item>
    <item>
      <title>I built a free self-hosted alternative to Pusher</title>
      <dc:creator>Jayden Robbins</dc:creator>
      <pubDate>Sun, 12 Apr 2026 17:44:18 +0000</pubDate>
      <link>https://forem.com/darknautica/i-built-a-free-self-hosted-alternative-to-pusher-1n92</link>
      <guid>https://forem.com/darknautica/i-built-a-free-self-hosted-alternative-to-pusher-1n92</guid>
      <description>&lt;p&gt;Every modern web app eventually needs real-time features. Live &lt;br&gt;
notifications, chat, presence indicators, collaborative editing. &lt;br&gt;
The moment you need it, every tutorial points you to Pusher.&lt;/p&gt;

&lt;p&gt;Pusher is great. It works, it's well documented, and the &lt;br&gt;
developer experience is solid. But the pricing hits hard once &lt;br&gt;
you have real traffic. 100 concurrent connections on the free &lt;br&gt;
tier. $49/month for 500. The bill scales faster than your app.&lt;/p&gt;

&lt;p&gt;The underlying technology — WebSockets — is open, well &lt;br&gt;
understood, and has been around for years. There was no good &lt;br&gt;
reason a polished self-hosted alternative didn't exist.&lt;/p&gt;

&lt;p&gt;So I built one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing Relay&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Relay is an open source, self-hostable WebSocket server written &lt;br&gt;
in Go. It gives you everything Pusher does at the cost of &lt;br&gt;
running a small server process on infrastructure you already own.&lt;/p&gt;

&lt;p&gt;The key design decision was full Pusher protocol compatibility. &lt;br&gt;
If you are already using Pusher, you can switch to Relay by &lt;br&gt;
changing one environment variable. No SDK changes, no refactoring, &lt;br&gt;
no migration script. Just point your app at your Relay server &lt;br&gt;
instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting started in 30 seconds&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6001:6001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_APP_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RELAY_APP_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-secret &lt;span class="se"&gt;\&lt;/span&gt;
  darknautica/relay:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the entire server setup. It starts on port 6001, &lt;br&gt;
serves a live dashboard at /dashboard, and is ready to &lt;br&gt;
accept WebSocket connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting from JavaScript&lt;/strong&gt;&lt;br&gt;
&lt;/p&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="nx"&gt;Relay&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@relayhq/relay-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&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;Relay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-key&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="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="s1"&gt;your-server.com&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;6001&lt;/span&gt;&lt;span class="p"&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;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-notification&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Publishing from Laravel&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require relayhq/relay-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/broadcasting.php&lt;/span&gt;
&lt;span class="s1"&gt;'default'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'relay'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;'connections'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'relay'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'driver'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'relay'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'host'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'RELAY_HOST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'port'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'RELAY_PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6001&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'key'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'RELAY_APP_KEY'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'secret'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'RELAY_APP_SECRET'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then your existing broadcast events just work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrderShipped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it supports&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public, private, and presence channels&lt;/li&gt;
&lt;li&gt;HMAC authentication for private and presence channels&lt;/li&gt;
&lt;li&gt;HTTP publish API for backend event publishing&lt;/li&gt;
&lt;li&gt;Built-in web dashboard with live connection stats and event log&lt;/li&gt;
&lt;li&gt;Channel history and replay for clients that reconnect&lt;/li&gt;
&lt;li&gt;Webhooks with HMAC-signed payloads&lt;/li&gt;
&lt;li&gt;Multi-app support via apps.json&lt;/li&gt;
&lt;li&gt;Rate limiting and graceful shutdown&lt;/li&gt;
&lt;li&gt;Official SDKs for Laravel, Node.js, Rails, and Django&lt;/li&gt;
&lt;li&gt;Single binary — no runtime, no JVM, no Node required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The comparison that matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pusher free tier gives you 100 concurrent connections and &lt;br&gt;
200k messages per day. Relay gives you unlimited everything &lt;br&gt;
for the cost of a $5/month VPS.&lt;/p&gt;

&lt;p&gt;For a startup with 300 concurrent users, that is the &lt;br&gt;
difference between $0 and $49/month. Over a year that is &lt;br&gt;
$588 — more than the server costs for the whole year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project is MIT licensed and lives on GitHub. Pre-built &lt;br&gt;
binaries for Windows, Mac, and Linux are available on the &lt;br&gt;
releases page. A Docker image is published to Docker Hub.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/DarkNautica/Relay" rel="noopener noreferrer"&gt;https://github.com/DarkNautica/Relay&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://darknautica.github.io/Relay" rel="noopener noreferrer"&gt;https://darknautica.github.io/Relay&lt;/a&gt;&lt;br&gt;
Docker: darknautica/relay&lt;/p&gt;

&lt;p&gt;Would love feedback from anyone who has dealt with the &lt;br&gt;
Pusher pricing wall. Happy to answer questions about the &lt;br&gt;
Go architecture or the protocol implementation.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>laravel</category>
      <category>go</category>
    </item>
  </channel>
</rss>
