Migrate from Pusher

Relay is wire-compatible with Pusher. Most apps can switch by changing configuration only — no code changes needed.

Step 1: Create a Relay App

Sign up at Relay and create a new app. Note your Key and Secret.

Step 2: Update Your Client

Point clients at your Relay host and ports (from RELAY_PUBLIC_WS_HOST / RELAY_PUBLIC_WS_PORT, or defaults). On this deployment, examples resolve to server.relayio.dev with wsPort 6001 and wssPort 443 when unset.

cluster: Relay is self-hosted — keep any placeholder string (e.g. mt1) so pusher-js / Echo stay happy; Relay ignores Pusher regions.

Using pusher-js

Change the constructor options to point to your Relay server:

// Before (Pusher)
const pusher = new Pusher('pusher-key', {
    cluster: 'us2',
});

// After (Relay)
const pusher = new Pusher('relay-app-key', {
    wsHost: 'server.relayio.dev',
    wsPort: 6001,
    wssPort: 443,
    forceTLS: typeof relayPublicWebSocketForceTls === 'function' ? relayPublicWebSocketForceTls() : (location.protocol === 'https:'),
    enabledTransports: ['ws', 'wss'],
    cluster: 'mt1',
});

Everything else stays the same — subscribe(), bind(), trigger() all work identically.

Using Laravel Echo

See the full guide: Laravel Echo with Relay.

// Before
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'pusher-key',
    cluster: 'us2',
});

// After (set window.Pusher from pusher-js first — see /docs/laravel-echo)
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'relay-app-key',
    wsHost: 'server.relayio.dev',
    wsPort: 6001,
    wssPort: 443,
    forceTLS: typeof relayPublicWebSocketForceTls === 'function' ? relayPublicWebSocketForceTls() : (location.protocol === 'https:'),
    encrypted: typeof relayPublicWebSocketForceTls === 'function' ? relayPublicWebSocketForceTls() : (location.protocol === 'https:'),
    disableStats: true,
    enabledTransports: ['ws', 'wss'],
    cluster: 'mt1',
});

Step 3: Update Your Server

REST API

Replace Pusher's trigger API URL with Relay's:

# Before
POST https://api-us2.pusher.com/apps/APP_ID/events

# After
POST https://your-relay-api-host/api/v1/apps/APP_ID/events

The request/response format is identical. Auth headers change from Pusher's HMAC to either:

  • X-Relay-Key + X-Relay-Secret (simplest)
  • Authorization: Bearer <app-token> (token-based)

Auth Endpoint

If you have a custom /broadcasting/auth endpoint, it works as-is. The HMAC signature format is the same (key:sha256(socket_id:channel, secret)).

What's Different

Feature Pusher Relay
WebSocket URL ws://ws-{cluster}.pusher.com ws://your-host:port/app/{key}
REST API https://api-{cluster}.pusher.com Your Relay API base (e.g. this app's REST routes)
Auth Pusher HMAC on query string Header-based or Pusher HMAC
Encrypted channels Supported Planned
Webhooks Dashboard config Per-app in dashboard
Message history Not available Built-in (cache + rewind)

What's Compatible

  • All channel types (public, private, presence)
  • Client events (client-*)
  • Subscription succeeded/member_added/member_removed events
  • Ping/pong keepalive
  • pusher-js and Laravel Echo client libraries
  • HMAC auth signature format