Appearance
Deployment And Scaling
Running The Worker
For driver: 'holo', run the worker as a supervised long-running process through your package manager:
bash
npx holo broadcast:workbash
pnpm dlx holo broadcast:workbash
yarn dlx holo broadcast:workbash
bunx holo broadcast:workRun it under your process manager (systemd, PM2, containers, orchestration platform). Both Bun and Node runtimes are supported for the worker process. See Deployment for the broader host decision when the web app runs on a serverless or edge platform.
Running In Production
- App server handles API,
/broadcasting/config, and/broadcasting/auth. - Websocket worker handles realtime transport and fan-out.
- Worker can run on the same host or a separate host.
Place websocket traffic behind a reverse proxy / load balancer and terminate TLS at the edge.
The health endpoint is public. The statistics endpoint returns 404 unless worker.statsEnabled is explicitly set to true. When enabled, restrict the statistics path through the reverse proxy, private network, or infrastructure authentication; the worker does not authenticate that operational endpoint.
Scaling
Redis-backed coordination is required for multi-node self-hosted websocket deployments. All worker instances must share the same Redis backend for pub/sub and presence synchronization.
Configure Redis Coordination
- Define a shared Redis connection in
config/redis.ts. - Configure broadcast scaling to use that shared Redis connection by name.
- Start multiple
broadcast:workprocesses; all must use the same Redis connection.
Example .env:
bash
REDIS_URL=
REDIS_HOST=10.0.0.25
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
BROADCAST_REDIS_CONNECTION=defaultExample config/redis.ts:
ts
import { env } from '@holo-js/config'
import { defineRedisConfig } from '@holo-js/kernel'
export default defineRedisConfig({
default: 'default',
connections: {
default: {
url: env('REDIS_URL') || undefined,
host: env('REDIS_HOST', '127.0.0.1'),
port: env('REDIS_PORT', 6379),
password: env('REDIS_PASSWORD'),
db: env('REDIS_DB', 0),
},
},
})Example config/broadcast.ts:
ts
import { defineBroadcastConfig } from '@holo-js/broadcast'
import { env } from '@holo-js/config'
export default defineBroadcastConfig({
default: env('BROADCAST_CONNECTION', 'reverb'),
connections: {
reverb: {
driver: 'holo',
key: env('BROADCAST_APP_KEY'),
secret: env('BROADCAST_APP_SECRET'),
appId: env('BROADCAST_APP_ID'),
options: {
host: env('BROADCAST_HOST', '127.0.0.1'),
port: env('BROADCAST_PORT', 8080),
scheme: env<'http' | 'https'>('BROADCAST_SCHEME', 'http'),
useTLS: env('BROADCAST_SCHEME', 'http') === 'https',
},
},
},
worker: {
allowedOrigins: [env('APP_URL', 'http://localhost:3000')],
scaling: {
driver: 'redis',
connection: env('BROADCAST_REDIS_CONNECTION', 'default'),
},
},
})Shared Redis connections resolve in this order:
urlclustershost
So if REDIS_URL is present, the worker uses that target. Otherwise it uses cluster settings when defined. Otherwise it falls back to host / port or a socket path.
Example process scaling:
bash
# node A
npx holo broadcast:work
# node B
npx holo broadcast:workIf each node has a different Redis target, presence and cross-node delivery will break.
Hosted Providers
driver: 'pusher' targets hosted providers. Pusher-compatible providers should be configured through the pusher driver connection shape.
Pusher-compatible providers typically require:
- app credentials
- host
- port
- scheme / TLS settings
Notifications Bridge
When both notifications and broadcast packages are installed, notifications on the built-in broadcast channel are forwarded automatically through the broadcast runtime.
This keeps notifications and realtime delivery aligned without extra bridge code in your app.