← Back to blog
MIGRATION GUIDE6 min read

Migrate from Pusher to NoLag

HB
Henco Burger
July 29, 2026

This post was written for NoLag Cloud, the hosted service, which was retired in October 2026. To run NoLag yourself, start with the self-hosting guide.

Pusher Channels popularised hosted realtime pub/sub, and a lot of apps still run on it. If you are moving to NoLag, the good news is that the mental model is almost identical: you subscribe to a channel, you bind to events, and a publisher sends data to everyone listening. This guide maps Pusher concepts onto NoLag and shows the same subscribe-and-publish flow in both.

Why teams move

NoLag keeps the pub/sub foundation you already rely on and adds a few things Pusher does not focus on:

  • Blueprint SDKs for chat, notifications, dashboards, tracking, and more, so common patterns are a few lines instead of a from-scratch build.
  • Presence built in, tracked per room, with lobbies for presence across rooms.
  • Open source, on your own infrastructure. Pusher runs its service for you; NoLag's kraken broker is Apache-2.0 software you run yourself as a single container, with no vendor lock-in and no bill that grows with your connection count.

Concept map

PusherNoLag
App key + clusterApp (a container for rooms and topics)
ChannelRoom + topic
channel.bind('event', cb)room.on('topic', cb)
pusher.trigger('channel', 'event', data)room.emit('topic', data)
Presence channelPresence (per room)
Private channel + auth endpointActors, access tokens, and per-topic ACL

Before: Pusher

import Pusher from 'pusher-js'

const pusher = new Pusher('APP_KEY', { cluster: 'eu' })

const channel = pusher.subscribe('chat')
channel.bind('message', (data) => {
  console.log('Received:', data)
})

// On your server, using the pusher server SDK:
// pusher.trigger('chat', 'message', { text: 'Hello!' })

After: NoLag

import { NoLag } from '@nolag/js-sdk'

// KRAKEN_URL is your broker, for example ws://localhost:8080/ws.
// The `chat` app and its `general` room are granted to both tokens in your
// auth config (kraken's auth.json or a @nolag/core project document).
const APP_SLUG = 'chat'

// Browser: subscribe as one actor
const client = NoLag('your_access_token', { url: KRAKEN_URL })
const room = client.setApp(APP_SLUG).setRoom('general')
room.on('message', (data) => {
  console.log('Received:', data)
})
// Subscribe in the connect handler: it runs after every reconnect too
client.on('connect', () => room.subscribe('message'))
await client.connect()

// Server (or any other authorised actor): publish. Publishers never receive
// their own messages, so publish from a different actor than the one listening.
const server = NoLag('service_access_token', { url: KRAKEN_URL })
await server.connect()
server.setApp(APP_SLUG).setRoom('general').emit('message', { text: 'Hello!' })

The shape is the same. The main differences are that NoLag groups topics inside a room (so you get natural namespacing and presence per room), access is controlled with actors and tokens rather than a per-channel auth endpoint, and any authorised actor can publish from the client side, not only your server. A NoLag publisher never hears its own message back, so append it to your UI locally when you send.

Auth: delete your auth endpoint

Pusher private and presence channels call back to an auth endpoint you host, on every subscribe. NoLag does not need one. A connection authenticates once with an actor access token, and what that actor can read or write is enforced at the broker with per-topic ACL, which kraken caches, so there is no auth round-trip on every subscribe. Every credential resolves to a typed actor, such as user, device, service or session.

For browser clients running against @nolag/core, mint a short-lived JWT (client token) on your backend, signed with a project signing key. Issuing one is a local signing step with no network call, and it expires on its own within minutes, so a long-lived secret never reaches the browser.

Presence

Pusher presence channels map to NoLag presence, tracked per room. A client joins the member list by calling room.setPresence(data), and everyone else gets presence:join, presence:leave and presence:update events on the client, plus room.fetchPresence() for the current member list, without standing up your own tracking.

Next steps

  • Follow the 5-minute quick start to connect for the first time.
  • If you are building chat, the chat blueprint gives you rooms, who-is-online presence, typing indicators and streamed messages out of the box. It keeps an in-memory cache of the messages it has seen; message history is yours to store.
  • Compare the platforms in more detail: NoLag vs Pusher.