← Back to blog
MIGRATION GUIDE6 min read

Migrate from Ably to NoLag

HB
Henco Burger
July 31, 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.

Ably is a mature realtime platform with rich delivery semantics. NoLag shares the same pub/sub foundation and presence model, so moving across is mostly a matter of renaming a few concepts. This guide maps Ably onto NoLag and shows the same flow in both.

Why teams move

NoLag keeps the realtime pub/sub and presence you rely on, and changes who runs it:

  • Open source, on your own infrastructure. The kraken broker is Apache-2.0 and runs as a single container, so there is no vendor lock-in and no per-message bill. The trade is real: Ably operates its service for you, and with NoLag you operate kraken yourself.
  • Blueprint SDKs for chat, notifications, dashboards, tracking, and more.

Concept map

AblyNoLag
API key / token authActor access tokens
ChannelRoom + topic
channel.subscribe('event', cb)room.on('topic', cb)
channel.publish('event', data)room.emit('topic', data)
channel.presencePresence (per room)
HistoryYour own store (NoLag is a messaging layer, not a database)

Before: Ably

import * as Ably from 'ably'

const ably = new Ably.Realtime('API_KEY')
const channel = ably.channels.get('chat')

channel.subscribe('message', (msg) => {
  console.log('Received:', msg.data)
})

channel.publish('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'

// One actor subscribes...
const receiver = NoLag('receiver_access_token', { url: KRAKEN_URL })
const inbox = receiver.setApp(APP_SLUG).setRoom('general')
inbox.on('message', (data) => {
  console.log('Received:', data)
})
// Subscribe in the connect handler: it runs after every reconnect too
receiver.on('connect', () => inbox.subscribe('message'))
await receiver.connect()

// ...and another publishes. Publishers never receive their own messages,
// so a single client that subscribes and emits will not log "Received".
const sender = NoLag('sender_access_token', { url: KRAKEN_URL })
await sender.connect()
sender.setApp(APP_SLUG).setRoom('general').emit('message', { text: 'Hello!' })

One behavioural difference from Ably is worth flagging before you port a test suite: an Ably client sees its own publishes, a NoLag actor never does. Append the sent message to your UI locally, and use a second actor in tests that expect to receive.

Presence and history

Ably presence maps to NoLag presence, tracked per room with join and leave events and a live member list. A client sets its presence on the room with room.setPresence(data) and listens for presence:join, presence:leave and presence:update on the client.

History is the one Ably feature that does not have a counterpart. NoLag is a messaging layer, not a database: nothing published while a client was disconnected is replayed when it comes back, and a fresh subscription receives no history. If you use Ably history to fill gaps after a disconnect or to render the last N messages on load, keep those messages in your own store and read it on connect. Subscribe in the connect handler, as in the sample above: it runs after every successful reconnect, and a self-hosted broker does not reliably restore subscriptions on its own.

Delivery guarantees

Ably is known for its delivery semantics. NoLag accepts quality-of-service levels 0, 1, and 2 per message, chosen on each emit. Be precise about what they cover: the level governs the hop between kraken and an MQTT broker when you run kraken against one, kraken's built-in broker does not use it, and the WebSocket leg between your client and kraken has a simpler contract. A publish can be acknowledged (pass a callback to emit and it resolves on the broker's published ack), nothing is resent for you, and there is no exactly-once guarantee end to end. Put an idempotency key in the payload of anything that must not be applied twice.

Authentication

Ably authenticates with API keys and tokens, often carrying capabilities in the token itself. NoLag centres permissions on the actor: every credential resolves to a typed actor (such as user, device, service or session), and its read and write access lives server-side as per-topic ACL rather than in each token. For browser clients running against @nolag/core, your backend mints a short-lived JWT (client token) signed with a project signing key, so long-lived credentials never leave your servers.

Next steps