Native install

Documentation menu
On this page
  1. Firewall ports
  2. SRS and SRT relay config

The native install is the recommended way to run Bunryu in production. It targets Debian/Ubuntu on x86_64 and sets up three systemd services:

Service Purpose
srs.service Native SRS binary, started as /usr/local/bin/srs -c /etc/bunryu/srs.conf
goryu.service Shared Goryu relay, started on UDP port 10081
bunryu.service Node.js dashboard/API, started from /opt/bunryu/dist/index.js

The installer downloads the official SRS release binary and a pinned goryu binary from the standalone live-miracles/goryu GitHub releases.

Startup is ordered so the Node.js control plane is reachable before SRS accepts publishes. srs.service waits for the unauthenticated readiness endpoint http://127.0.0.1:8080/api/ready before starting; this prevents SRS publish hooks from racing the dashboard/API process during boot.

Production install:

sudo apt-get update -q && sudo apt-get install -y -q git  # in case git is not installed
sudo git clone https://github.com/live-miracles/bunryu /opt/bunryu
sudo bash /opt/bunryu/scripts/server-install.sh

Set server timezone (optional, for readable log timestamps):

SRS and journald log timestamps in the system's local timezone, which defaults to UTC on most fresh servers. Set it to your local timezone so log times make sense at a glance, e.g. for IST (Asia/Kolkata):

sudo timedatectl set-timezone Asia/Kolkata

List available timezone names with timedatectl list-timezones. No reboot is needed, but already-running services may have the old timezone cached in memory, so restart them to apply it to new log lines immediately:

sudo systemctl restart srs.service bunryu.service goryu.service

Update an installed server:

sudo bash /opt/bunryu/scripts/server-install.sh

Stop services:

sudo bash /opt/bunryu/scripts/server-down.sh

Open the dashboard: http://SERVER_IP:8080 — the installer prints the generated password on first install (it is also stored as dashboard_password in bunryu.json).

After the first login, set Settings → Public Host to the address your encoders use, so the publish URLs shown in the dashboard are correct. To reach the dashboard securely from outside, see Remote access.

Firewall ports

Default ports from srs.conf and goryu.json:

Port Protocol Purpose
21935 TCP RTMP input (non-default port to avoid 1935 scanner noise)
10080 UDP SRT input (passphrase required)
10081 UDP SRT bonding input, also accepts plain SRT (passphrase required)
8080 TCP Dashboard + API

Do not expose 8080 if the dashboard is served through a tunnel, or 8081 (relay status). The SRS HTTP API (1985) is bound to 127.0.0.1 in srs.conf, and SRS's publish/play hooks go to the app's separate loopback-only hook port (hook_port, default 8082), so neither can be reached from outside even with a permissive firewall. Neither port needs a firewall rule; check with ss -tlnp after install that 1985 and 8082 listen only on 127.0.0.1.

SRS and SRT relay config

The repository copies both runtime config files during install:

  • /etc/bunryu/srs.conf
  • /etc/bunryu/goryu.json

SRS only reads its config at startup, and Goryu only reads its JSON config file at startup.

The repo configs ship a public default SRT passphrase; on first install the installer generates a random per-server secret and writes it to both files. On reinstall it leaves each file's passphrase exactly as deployed — it doesn't compare, regenerate, or sync the two. SRS rejects SRT connections without the passphrase at the handshake, for publish and play alike; the dashboard's SRT publish URLs include it.

To change or disable it, edit both files by hand and restart SRS and the relay: passphrase ; in srs.conf, "passphrase": "" in goryu.json. An empty value in both disables the passphrase check entirely — SRS and the relay treat empty the same as unset.

Ingest is further locked down by SRS HTTP hooks handled by the app: on_publish rejects unknown stream keys, and on_play rejects any play not from loopback (only the app's own FFmpeg ever pulls streams), so the public ports are ingest-only. See Security for why rejected attempts are logged but not converted into firewall bans.

Edit this page on GitHub