Migrate from PubNub to Vask (Laravel). SDK Swap, Open Protocol.
PubNub to Vask is an SDK swap, not a host swap. Keep the business logic that reacts to messages; replace the transport with Laravel Broadcasting, laravel-echo, and pusher-js.
Migration summary
At a glance
- Type
- SDK-swap migration
- Typical time
- 1-3 days for most Laravel apps
- Server SDK
- Laravel Broadcasting / pusher-php-server
- Client SDK
- laravel-echo + pusher-js
- Auth changes
- replace PubNub tokens with Laravel channel auth
- Rollback
- feature flag or deploy back to the PubNub publish/subscribe path
Files touched
- composer.json
- .env
- config/broadcasting.php
- routes/channels.php
- resources/js/bootstrap.js
Get started
Realtime made simple.
Free Tier: 500K broadcasts/mo and 100 concurrent across unlimited apps. $10/mo when you outgrow it.
Feature mapping
Vask targets the Pusher Channels protocol surface. Map PubNub features before changing code.
| PubNub concept | Vask / Laravel equivalent | Migration note |
| ------------------------- | ------------------------------------------- | --------------------------------------------------------------- |
| Publish message | broadcast(new Event(...)) | Move publish call sites to Laravel events. |
| Subscribe to channel | Echo.channel(...).listen(...) | Move handlers from PubNub listeners to Echo listeners. |
| Private channel access | routes/channels.php auth callback | Replace PubNub tokens or PAM checks with Laravel authorization. |
| Presence | Echo.join(...).here().joining().leaving() | Same user-awareness goal, different API semantics. |
| Client-originated events | Echo whisper on private/presence channels | Use only for ephemeral client events such as typing. |
| Functions on publish path | Laravel service, listener, or queued job | Move enrichment/filtering into your application. |
| Message history / storage | Your database or existing persistence layer | Not part of this channels cutover. |
Channel and event mapping
PubNub usually combines a flat channel name with a message payload field such as type. In Laravel Broadcasting, channel name and event name are separate.
| PubNub shape | Laravel / Echo call | Pusher wire channel |
| ------------------------ | -------------------------- | ------------------- |
| orders | Echo.channel('orders') | orders |
| user.123 private feed | Echo.private('user.123') | private-user.123 |
| room.456 presence room | Echo.join('room.456') | presence-room.456 |
Use broadcastAs() for explicit event names. Echo listeners for custom names need the leading dot:
public function broadcastAs(): string
{
return 'order.created';
}Echo.channel('orders').listen('.order.created', (event) => {
handleOrderCreated(event);
});If your PubNub payload was { type: 'order.created', order_id: 123 }, the Pusher event name becomes order.created and the handler receives the payload directly.
Out of scope
Keep these PubNub surfaces out of the Vask channels cutover:
| PubNub surface | Recommended path | | ----------------------------- | ----------------------------------------------------- | | Functions | Move logic into Laravel before broadcasting. | | Access Manager / PAM | Replace with Laravel policies and channel auth. | | Message Persistence / history | Store messages in your database before broadcasting. | | Files | Keep existing file storage or move to object storage. | | Mobile Push | Keep your push provider or migrate separately. |
Progressive rollout
Migrate by feature, not by whole application.
- Inventory PubNub channels, message types, presence usage, and any Functions on the publish path.
- Pick one low-risk feature or channel family.
- Add the Vask Broadcast event and Echo listener while PubNub remains live for everything else.
- Optionally dual-publish for that feature in staging or behind a feature flag.
- Switch the client surface to Echo, verify, then remove that feature's PubNub publish and subscribe path.
- Repeat for the next feature.
This keeps rollback local. A failed feature can return to the PubNub path without touching unmigrated features.
Laravel setup
Use the Vask installer if you want the shortest Laravel Broadcasting setup:
composer remove pubnub/pubnub-php
composer require vask/laravel
php artisan vask:installManual setup is also fine:
php artisan install:broadcasting --pusher
composer require pusher/pusher-php-serverSet Vask credentials in .env. If your SDK asks for an app id, use the Vask app key. Vask does not issue a separate customer-facing app id.
BROADCAST_CONNECTION=pusher
PUSHER_APP_ID=your_vask_key
PUSHER_APP_KEY=your_vask_key
PUSHER_APP_SECRET=your_vask_secret
PUSHER_HOST=wss.vask.dev
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"Confirm the pusher connection points at Vask:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'host' => env('PUSHER_HOST', 'wss.vask.dev'),
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
'encrypted' => true,
'useTLS' => true,
],
],Server publish
Replace PubNub publish calls:
$pubnub->publish()
->channel('orders')
->message(['type' => 'order.created', 'order_id' => $id])
->sync();With a Laravel Broadcast event:
use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class OrderCreated implements ShouldBroadcast
{
/**
* @param array{order_id: int, status: string} $order
*/
public function __construct(public array $order) {}
public function broadcastOn(): array
{
return [new Channel('orders')];
}
public function broadcastAs(): string
{
return 'order.created';
}
/**
* @return array{order_id: int, status: string}
*/
public function broadcastWith(): array
{
return $this->order;
}
}broadcast(new OrderCreated([
'order_id' => $id,
'status' => 'created',
]));Client subscribe
Replace PubNub listeners:
const pubnub = new PubNub({ publishKey, subscribeKey });
pubnub.addListener({
message: ({ message }) => {
if (message.type === 'order.created') {
handleOrderCreated(message);
}
},
});
pubnub.subscribe({ channels: ['orders'] });With Echo over pusher-js:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
wsHost: import.meta.env.VITE_PUSHER_HOST,
wsPort: import.meta.env.VITE_PUSHER_PORT,
wssPort: import.meta.env.VITE_PUSHER_PORT,
forceTLS: true,
enabledTransports: ['ws', 'wss'],
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
});
window.Echo.channel('orders').listen('.order.created', (event) => {
handleOrderCreated(event);
});Your handler logic stays. The subscription API changes.
Private and presence channels
Move PubNub access checks to Laravel channel authorization:
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('user.{id}', function (User $user, int $id): bool {
return $user->id === $id;
});
Broadcast::channel('room.{id}', function (User $user, int $id): array|false {
if (! $user->canJoinRoom($id)) {
return false;
}
return ['id' => $user->id, 'name' => $user->name];
});window.Echo.private(`user.${userId}`).listen(
'.notification.sent',
handleNotification,
);
window.Echo.join(`room.${roomId}`)
.here(setPresentUsers)
.joining(addPresentUser)
.leaving(removePresentUser);Verify
Before production cutover:
- Trigger one public event and confirm the browser receives it from
wss.vask.dev. - Trigger one private event and confirm unauthorized users get a failed auth response.
- Join one presence channel and confirm
here,joining, andleavingmatch your UI expectations. - Compare payload shape against the old PubNub handler.
- Test reconnect after network interruption.
- If dual-publishing, confirm duplicate events do not reach production users.
Rollback
This is not an env-only rollback. Keep the PubNub code path until each migrated feature is stable.
For a migrated feature, rollback by disabling the Echo subscription, restoring the PubNub subscription, and sending server publishes back through the PubNub SDK. If you use feature flags, keep one flag for server publish path and one for client subscribe path so you can unwind safely.
Gotchas
- Payload envelope changes. PubNub listeners receive a
messageenvelope. Echo listeners receive the event payload directly. - Custom event names need a dot. If Laravel
broadcastAs()returnsorder.created, listen with.order.created. - Presence state is not method-compatible. PubNub
hereNow()andgetState()map to Echo callbacks and your own persisted state. - Functions must move first. If a PubNub Function mutates an event, move that logic into Laravel before switching the channel.
Where to go next
- Read the PubNub vs Vask comparison for product-surface and billing differences.
- Run the fan-out calculator against your workload.
- Check the Laravel docs for
vask:doctor, webhooks, and the local round-trip demo.
If your migration runs into something this page does not cover, email [email protected] and you will get an answer from someone who can read your code.
- Which PubNub features are unsupported?
- Vask covers the Pusher Channels surface: public channels, private channels, presence channels, broadcasts, client events, and channel auth. PubNub Functions, Access Manager, Files, Mobile Push, message history, and storage are separate product surfaces. Keep or replace those outside this channels migration.
- Can we migrate one feature at a time?
- Yes. Move one feature, channel family, or screen at a time. Keep PubNub running for untouched features, optionally dual-publish during validation, then switch that feature's client subscription to Echo and remove the old PubNub path after it is stable.
- Does presence work the same way as PubNub presence?
- The behavior maps, but the API does not. PubNub hereNow and state calls become Echo presence callbacks such as here, joining, and leaving, backed by Laravel channel authorization. Re-test member identity, join/leave timing, and any custom state you previously stored in PubNub.
- What should we test before production cutover?
- Test one public channel, one private channel, one presence channel, failed auth, reconnect behavior, payload shape, duplicate delivery during any dual-publish window, and rollback to the PubNub client path. Run the test from the same browser/runtime mix your users use.
Get going
Replace the PubNub SDK feature by feature, keep the message-handling logic, and move delivery to the open Pusher Channels protocol at the Vask edge.
Make the switch
Open protocol. Edge delivery. Done.
Replace the PubNub SDK, keep your application logic, and roll out one feature at a time.