A web control panel for librtmp2-server. Create streams, copy OBS/playback URLs, manage keys, and view live bitrate, codec, resolution, RTT, uptime, and viewer statistics.
Project status: active alpha. Use it for evaluation, development, and tested self-hosted deployments. Pin image versions and validate your complete workflow before critical production use.
- Website: openrtmp.org
- Five-minute guide: openrtmp.org/quickstart/
- Server: OpenRTMP/librtmp2-server
The standalone quickstart uses prebuilt server and panel images. It does not require a neighboring librtmp2-server source checkout.
Requirements: Docker with Docker Compose, OpenSSL, and Python 3 for secret generation.
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
cd librtmp2-server-panel
API_TOKEN="$(openssl rand -hex 32)"
PANEL_SECRET="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
PANEL_PASSWORD="$(openssl rand -base64 24 | tr -d '\n')"
cat > .env <<EOF
LRTMP2_API_TOKEN=${API_TOKEN}
LRTMP2_DOMAIN=localhost
LRTMP2_STATS_URL=http://localhost:8080
USERNAME=admin
PASSWORD=${PANEL_PASSWORD}
SECRET_KEY=${PANEL_SECRET}
REQUIRE_LOGIN=True
EOF
docker compose -f compose.quickstart.yml up -d
printf 'Panel: http://localhost:8000\nLogin: admin\nPassword: %s\n' "${PANEL_PASSWORD}"Open http://localhost:8000, sign in, and create a stream. The panel then shows the exact publish, playback, and statistics URLs.
For OBS, the default values are:
- Server:
rtmp://localhost:1935/live - Stream key: the generated
publish_keyshown by the panel
Health checks:
curl http://localhost:8080/api/v1/health
docker compose -f compose.quickstart.yml psStop the stack without deleting its SQLite data:
docker compose -f compose.quickstart.yml downFor an internet-facing deployment, replace localhost with the public hostname, put the HTTP services behind HTTPS, restrict port 8080, and pin the server/panel image tags instead of tracking latest.
LRTMP2_API_TOKEN is shared by the panel and server. On the server's first startup, the process environment value is stored in SQLite. If no value is supplied, the server generates a token and prints it once to its logs.
The server intentionally does not read this secret from its normal server .env configuration file. Docker Compose passes it as a real process environment variable. After the database has been initialized, changing only the Compose .env value does not automatically rotate the token stored in SQLite.
Copy .env.example to .env for a manual setup and adjust:
| Variable | Description | Required |
|---|---|---|
LRTMP2_API_URL |
Internal/server-side base URL of librtmp2-server | Yes outside the quickstart stack |
LRTMP2_STATS_URL |
Browser-reachable API URL used in copied stats links | No |
LRTMP2_API_TOKEN |
Shared bearer token seeded into the server on first startup | Yes |
LRTMP2_DOMAIN |
Public host/IP clients use for RTMP URLs; IPv6 literals are bracketed automatically | Yes |
LRTMP2_RTMP_PORT |
Public RTMP port, default 1935 |
No |
LRTMP2_RTMPS_PORT |
Public RTMPS port, default 1936 |
No |
LRTMP2_APP |
Default RTMP application, default live |
No |
REQUIRE_LOGIN |
Enable panel login, default True |
No |
USERNAME / PASSWORD |
Panel administrator credentials | When login is enabled |
SECRET_KEY |
Flask session secret | Yes |
RATELIMIT_STORAGE_URI |
Shared Flask-Limiter backend; quickstart uses Redis | No |
PANEL_PUBLIC_URL |
Public panel URL, used for secure-cookie detection | No |
SESSION_COOKIE_SECURE |
Force secure cookies when served over HTTPS | No |
TRUSTED_PROXY_COUNT |
Trusted value count used independently for X-Forwarded-For and X-Forwarded-Proto; default 0 ignores both |
No |
TRUSTED_PROXY_IPS |
Comma-separated proxy IPs/CIDR ranges trusted for rate-limit client IP selection | When TRUSTED_PROXY_COUNT > 0 |
Keep TRUSTED_PROXY_COUNT=0 when port 8000 is directly reachable. Enable it only when the panel is reachable exclusively through trusted proxies.
ProxyFix applies the configured count independently to X-Forwarded-For and X-Forwarded-Proto. Therefore every trusted proxy in the chain must append or normalize both headers so they contain the same number of trusted values. Whenever TRUSTED_PROXY_COUNT > 0, TRUSTED_PROXY_IPS must also list the trusted proxies' IPs/CIDR ranges — the panel refuses to start without it. Rate limiting only buckets by the forwarded X-Forwarded-For address when the direct TCP peer is one of these trusted proxies; otherwise it falls back to the peer's own address, so a client reaching the panel directly can't spoof X-Forwarded-For to bypass per-IP rate limits. A common safe one-proxy setup is:
PANEL_PUBLIC_URL=https://panel.example.com
TRUSTED_PROXY_COUNT=1
TRUSTED_PROXY_IPS=172.18.0.0/16proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;For a multi-proxy chain, normalize both headers at each trusted hop or at the final ingress so the configured count is valid for both headers. Do not set TRUSTED_PROXY_COUNT=2 when X-Forwarded-For contains two trusted values but X-Forwarded-Proto contains only one. Never enable forwarded-header trust while clients can bypass the proxy and connect directly to the panel.
The repository's docker-compose.yml is intended for development and builds the server from a sibling checkout:
parent/
├── librtmp2-server/
└── librtmp2-server-panel/
cd parent
git clone https://github.com/OpenRTMP/librtmp2-server.git
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
cd librtmp2-server-panel
cp .env.example .env
# Set LRTMP2_API_TOKEN, PASSWORD, SECRET_KEY, and LRTMP2_DOMAIN.
docker compose up -dUse compose.quickstart.yml when you only want the published images.
When a server already exists:
docker run -d \
--name librtmp2-server-panel \
-p 8000:8000 \
-e LRTMP2_API_URL=http://<server-host>:8080 \
-e LRTMP2_DOMAIN=<public-host-or-ip> \
-e LRTMP2_API_TOKEN=<server-token> \
-e PASSWORD=<panel-password-12-chars-or-more> \
-e SECRET_KEY=<random-32-plus-char-secret> \
ghcr.io/openrtmp/librtmp2-server-panel:latestPrebuilt multi-architecture images are published for amd64, arm64, and riscv64. Available tags include release versions and the moving latest, beta, and alpha channels.
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Edit the required values, then:
export $(grep -v '^#' .env | xargs)
python3 app.py- Create and delete streams through the server REST API
- Copy publish URL, stream key, play URL, and stats URL
- Live per-stream bitrate, resolution, codec, uptime, RTT, and viewer statistics
- Blurred key display with explicit copy actions
- Login protection, CSRF protection, rate limiting, and encrypted key storage
- RTMP and conditional RTMPS URL generation based on live server health data
Use GitHub Issues for reproducible bugs and feature requests. General architecture and usage discussions belong in OpenRTMP Discussions.
Contributions are welcome. Please include reproduction steps, screenshots for UI changes, and tests where practical.