Bunryu is a self-hosted restreaming server for long live events. It takes live inputs over RTMP, SRT and bonded SRT, manages each one as a pipeline, and forwards every pipeline to any number of RTMP, RTMPS or SRT destinations (outputs), such as YouTube, Facebook, Twitch, a CDN or another server.
It is built for reliability first: one failing destination, input or FFmpeg process never takes down the others, stuck outputs are restarted by watchdogs, and every failure leaves enough evidence to explain it afterwards.
How it works
OBS / ffmpeg ──RTMP────────► SRS (21935) ──FFmpeg──► YouTube / Facebook / ...
──SRT─────────► SRS (10080) ──FFmpeg──► rtmp:// or srt://
──SRT bonding─► goryu (10081) ──► SRS
| Component | Description |
|---|---|
| SRS | Ingest broker — accepts RTMP and SRT streams |
| goryu | Standalone bonded SRT relay (GitHub release in prod, sibling repo in dev) |
| Node.js app | REST API + dashboard on port 8080 |
| FFmpeg | One process per output, plus one per shared encode, spawned and managed by the app |
| SQLite | Persistent state for pipelines, outputs, stream keys, settings |
Each output is its own FFmpeg process that pulls the input from SRS over loopback and pushes it to one destination, so outputs fail and recover independently. Outputs of a pipeline that need the same encode instead copy from one shared encode. See Reliability for the watchdogs that supervise them.
Capacity and limits
The server is built and operated for the envelope below. It has not been tested or designed for anything beyond it — treat these as the supported ceiling, not a target to exceed :)
| Limit | Supported ceiling |
|---|---|
| Inputs (pipelines) | up to 50 |
| Outputs (forwards) | up to 500 total — enforced: creating a 501st returns 409 (shared encodes are not counted) |
| Distinct custom (transcoding) encodes | only a few at a time — see below |
| Parallel dashboard clients | up to ~10 |
Past these figures, expect host CPU/RAM/network and the number of concurrent FFmpeg processes (one per output, plus one per shared encode) to become the limiting factors well before the dashboard or API does.
Keep almost all outputs in copy mode. A copy output is a passthrough — it
remuxes the input and forwards it for a small fraction of a core (about 0.5% of a
vCPU on an RTMP input, 2–3% on an SRT input), so hundreds can run on modest
hardware. Selecting an audio track re-encodes the audio, a few percent more per
output. A custom encoding (720p, 1080p, vertical_rotate, …) makes FFmpeg
transcode the video, which is CPU-intensive: 0.6–2.5 vCPUs per encode on a GCP n2,
depending on the profile and the content. On a 4-vCPU machine one 1080p encode
alone takes about two thirds of the host. See Sizing a server for
the measured costs and example VM sizes. Outputs on the same pipeline with the same
encode (the same video encoding and the same single audio track) share one
shared encode, so CPU is counted once per
distinct encode per pipeline, not once per output. Only a handful of distinct
custom encodes should run at any one time; everything else should be copy. Putting many outputs
into custom-encoding mode will saturate the CPU long before the 500-output ceiling
and starve the copy outputs and the dashboard alike.
Previews copy the input's video, so an open preview costs only a few percent of one core, but each viewer receives the input's full bitrate. See Preview.
Parallel dashboard clients. Health is computed once every 5s and shared by all clients, so extra browser tabs do not multiply SRS/SRT-FFprobe work. API responses are gzip-compressed, and config changes from another session are picked up automatically via the health poll's config revision. Around 10 simultaneous dashboard clients is fine; higher counts are not tuned or tested.
Dashboard on a phone. The dashboard is designed for a desktop browser. Below 1024 px wide the three columns stack into one (pipeline list, then the input panel, then Outputs), the page scrolls normally, charts show one per row, and wide tables scroll sideways inside their card. This is enough to check state or start and stop an output from a phone; it is not tuned for editing, and drag-to-reorder does not work on touch screens.
Is it a good fit?
Good fit:
- Events that stream one or more feeds to many platforms at once.
- Encoders on unreliable links that need SRT or bonded SRT.
- Multi-language streams with live interpreters.
- Teams that want to self-host and keep their own logs.
Not designed for:
- Transcoding ladders or large numbers of encoded outputs.
- Serving viewers directly (it forwards to platforms and CDNs).
- Recording and VOD.
- More than about 50 inputs or 500 outputs on one server.