Run it yourself
open·broadcast is open-core: the receiver, encoder and bonding router are free software under AGPL-3.0. Everything the hosted service runs, you can run on your own VPS — the encoder ⇄ receiver contract is identical either way. This guide gets a receiver taking a bonded feed and fanning it out to your destinations.
This is a technical setup for broadcast engineers — you'll need a Linux VPS, Docker, and comfort on the command line. Community support is on GitHub; there's no hand-holding tier (that's what the hosted service will be for).
How it fits together
Your encoder sends one bonded RIST feed — multiple WAN uplinks presented as RIST peers on a single port — to a receiver you run. The receiver does a copy-only fan-out (no re-encode) to your destinations (RTMP/RTMPS/SRT/RIST). A bonding router is optional but is what turns several cheap uplinks into one resilient link.
# encoder ──▶ [bonding router] ──▶ your receiver (VPS) ──▶ RTMP · SRT · RISTRun the receiver
The receiver is a headless C++ service. The quickest path is the published container image; it idles until an encoder (or curl) starts a session.
docker run -d --name obr-receiver --restart unless-stopped \
-p 8080:8080 -p 5000:5000/udp \
-e OBR_TOKEN="$(openssl rand -hex 32)" \
-e OBR_PSK="$(openssl rand -hex 16)" -e OBR_PSK_AES=256 \
ghcr.io/patcarter883/open-broadcast-receiver:latest- 8080 — control API (bearer-token REST). 5000/udp — RIST ingest.
- OBR_TOKEN — control-API bearer token. Keep it set; anyone who reaches the control port with it can redirect your stream.
- OBR_PSK / OBR_PSK_AES — pre-shared key for encrypted RIST ingest.
- Optional: OBR_RECORD_DIR to archive sessions as verbatim TS, OBR_IDLE_TIMEOUT to auto-stop idle sessions.
The control port speaks plain HTTP inside the container. Put nginx/Caddy with TLS in front of :8080, or firewall it to your management network. Secrets never travel in URLs — tokens and PSKs go in headers and dedicated fields only.
Prefer to build from source (or read every flag)? See the receiver README — CMake ≥ 3.28, GStreamer ≥ 1.28, vendored librist.
Point an encoder at it
Run the open·broadcast encoder (branch v2, FLTK desktop app) on the source machine. Give it your receiver's control URL and OBR_TOKEN, take NDI / SDP / MPEG-TS in, and it drives the receiver over the same REST contract the hosted service uses. Any RIST-ADVANCED sender that speaks the contract works too.
Bond your uplinks (optional but recommended)
To bond several WANs (Starlink + LTE + fixed), run luci-app-rist2rist on an OpenWrt router: one RIST feed in, one RIST peer out per WAN interface. The receiver sees the paths as bonded peers on the session port and rides out any one of them dropping — no reconnect. Without a bonding router you can still send over a single link; you just lose the resilience.
Start a stream
The encoder does this for you, but here it is directly — POST /start (schema 2):
curl -X POST https://your-receiver:8080/start \
-H "Authorization: Bearer $OBR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"schema_version": 2,
"session_id": "demo-1",
"ingest": { "bandwidth": 8000 },
"source": { "codec": "h264" },
"outputs": [
{ "id": "yt", "type": "rtmp",
"url": "rtmp://a.rtmp.youtube.com/live2",
"key_or_streamid": "YOUR-STREAM-KEY" }
]
}'
# watch it: /status and /stats (per-peer bond telemetry); stop with POST /stopOutputs reconnect forever with backoff, so a platform ingest hiccup mid-event heals without you touching anything. Destination validation blocks private IP ranges by default (SSRF hygiene); opt out with --egress-allow-private if you restream to your own LAN.
The repositories
All first-party code is AGPL-3.0-or-later. If you run a modified receiver as a network service, the licence's network clause applies — share your changes.