DEV Community

Jayden Robbins
Jayden Robbins

Posted on

I built a free self-hosted alternative to Pusher

Every modern web app eventually needs real-time features. Live
notifications, chat, presence indicators, collaborative editing.
The moment you need it, every tutorial points you to Pusher.

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

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

So I built one.

Introducing Relay

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

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

Getting started in 30 seconds

docker run -d -p 6001:6001 \
  -e RELAY_APP_KEY=my-key \
  -e RELAY_APP_SECRET=my-secret \
  darknautica/relay:latest
Enter fullscreen mode Exit fullscreen mode

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

Connecting from JavaScript

import Relay from '@relayhq/relay-js'

const relay = new Relay('my-key', {
  host: 'your-server.com',
  port: 6001,
})

const channel = relay.subscribe('notifications')

channel.bind('new-notification', (data) => {
  console.log(data.message)
})
Enter fullscreen mode Exit fullscreen mode

Publishing from Laravel

composer require relayhq/relay-php
Enter fullscreen mode Exit fullscreen mode
// config/broadcasting.php
'default' => 'relay',
'connections' => [
    'relay' => [
        'driver'  => 'relay',
        'host'    => env('RELAY_HOST', '127.0.0.1'),
        'port'    => env('RELAY_PORT', 6001),
        'key'     => env('RELAY_APP_KEY'),
        'secret'  => env('RELAY_APP_SECRET'),
    ],
],
Enter fullscreen mode Exit fullscreen mode

Then your existing broadcast events just work.

broadcast(new OrderShipped($order));
Enter fullscreen mode Exit fullscreen mode

What it supports

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

The comparison that matters

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

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

Try it

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

GitHub: https://github.com/DarkNautica/Relay
Docs: https://darknautica.github.io/Relay
Docker: darknautica/relay

Would love feedback from anyone who has dealt with the
Pusher pricing wall. Happy to answer questions about the
Go architecture or the protocol implementation.

Top comments (0)