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-jsand Laravel Echo client libraries- HMAC auth signature format