Deployment guide

Broadcast Box

Overview

Broadcast Box is an open source WebRTC live streaming server. It implements WHIP for ingest and WHEP for playback, the two standards that carry video over the same real-time transport a video call uses. The delay between something happening in front of the camera and a viewer seeing it is a fraction of a second, rather than the ten to thirty seconds that a traditional segmented streaming pipeline imposes.

You broadcast from OBS Studio or any encoder that speaks WHIP, or directly from a browser tab. Viewers watch in any modern browser with no plugin, no app and no third-party streaming service in the video path. This guide covers deploying and configuring Broadcast Box on Ubuntu 24.04 on Azure from the SecureAnyCloud Azure Marketplace image.

What you get:

  • Compiled from the pinned upstream release. The image is compiled at build time, and the compilers and source tree are then removed. What ships is a single static Go binary at /usr/local/bin/broadcast-box plus the built web interface. It runs under systemd as the non-root broadcastbox user.
  • Publishing is closed by default. This is a deliberate departure from the upstream default, and it is the most important thing to understand about this image. Broadcast Box normally runs with STREAM_PROFILE_POLICY=ANYONE_WITH_RESERVED, under which any anonymous caller may publish to any stream key that has not been reserved. On a virtual machine with a public address, that is an open media relay: a stranger who finds your instance can push video through your bandwidth. This image ships STREAM_PROFILE_POLICY=RESERVED instead.
  • Every publish requires a bearer token that belongs to a reserved stream profile. Exactly one such profile exists, and it is created on this VM's first boot. An anonymous publish attempt, or one with a wrong credential, is refused.
  • The admin portal requires a separate token, also generated on first boot. The portal and the whole /api/admin/* surface return HTTP 401 without it.
  • No credential of any kind is baked into the image. Two machines launched from the same image get different tokens.
  • Playback is open to anyone holding the stream key, which is what makes a player link shareable. The stream key is random per VM, so it is not guessable, but treat it as a capability: anyone you give it to can watch. Mint additional reserved profiles from the admin portal if you need to revoke access for one audience without disturbing another.
  • Broadcast Box, compiled from the pinned upstream release and run under systemd as the unprivileged broadcastbox user (broadcast-box.service).
  • The web interface on port 80: stream discovery, browser publishing, a player with cinema mode, a statistics page and an admin portal.
  • All WebRTC media muxed onto a single UDP port (3478), so you open one UDP port rather than an ephemeral range.
  • A per-VM admin token and a per-VM reserved stream profile, generated on first boot and written to a root-only credentials file.
  • A shipped round-trip self-test that proves the auth model end to end, including that an anonymous publish is refused.
  • Ubuntu 24.04 LTS, patched at build time, with unattended security upgrades enabled.

Prerequisites

  • An active Azure subscription.
  • An SSH public key.
  • A VNet and subnet in the target region.
  • A subscription to the Broadcast Box listing on Azure Marketplace.
  • Network Security Group rules that allow TCP 22 (admin), TCP 80 (web interface and WHIP/WHEP signaling) and UDP 3478 (all WebRTC media).
  • The recommended virtual machine size is Standard_B2s (2 vCPU, 4 GB RAM), which is ample for a handful of concurrent viewers. Broadcast Box forwards media without transcoding, so its cost is bandwidth rather than CPU. Increase the size for large viewer counts.
1

Deploy from the Azure Portal

In Azure Marketplace, search for Broadcast Box, select the SecureAnyCloud publisher, and choose Create.

Configure the Network Security Group to allow TCP 80 and UDP 3478 from the networks your broadcasters and viewers will connect from, and TCP 22 for administration. For internet exposure, front port 80 with TLS (see Step 10).

2

Deploy from the Azure CLI

RG="broadcast-box-prod"; LOCATION="eastus"; VM_NAME="broadcastbox1" IMAGE_URN="sac1647283583153:broadcast-box-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:broadcast-box-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard \
  --os-disk-delete-option Delete --nic-delete-option Delete
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 3478 --protocol Udp --priority 1002

Both rules matter. Opening TCP 80 alone produces the classic WebRTC failure. The page loads and the player appears to connect, but no video ever arrives, because signaling succeeds over HTTP while the media plane is silently blocked. If a stream never starts, check the UDP rule first.

3

First boot

On first boot, the image generates a unique admin token, stream key and publish bearer token for this VM. It writes the single reserved stream profile, records the machine's public address so that WebRTC advertises a reachable path, and arms the service. Nothing is shared between VMs.

Read your credentials over SSH:

sudo cat /root/broadcast-box-credentials.txt
# Broadcast Box — Per-VM Instance Info
# Generated: Sun Jul 19 13:34:42 UTC 2026
#
# There are NO default credentials. Every value below was generated uniquely on
# THIS VM's first boot and was never baked into the image. Keep this file secret # (root only). # broadcastbox.url=http://<your-vm-ip>/ broadcastbox.admin_url=http://<your-vm-ip>/admin broadcastbox.admin_token=**** broadcastbox.stream_key=cimg-95680abd4df2fa38 broadcastbox.stream_token=**** broadcastbox.whip_endpoint=http://<your-vm-ip>/api/whip broadcastbox.player_url=http://<your-vm-ip>/cimg-95680abd4df2fa38

The file also records the OBS settings and the network rules you need. Keep it secret: the stream token is your publishing credential.

4

Confirm the server is running

systemctl is-active broadcast-box ss -tln | grep -E ':80 |:22 ' ss -uln | grep ':3478 '
active LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 4096 [::]:22 [::]:* LISTEN 0 4096 *:80 *:* UNCONN 0 0 10.0.0.8:3478 0.0.0.0:*

The listener set is deliberately small. The Broadcast Box HTTP server on port 80 and sshd on port 22 are the only externally bound TCP services, and UDP 3478 carries all WebRTC media for both publishing and playback.

No database is installed, because Broadcast Box is stateless. Streams are live sessions held in memory, and nothing is recorded to disk.

The server binds port 80 as a non-root user through a single Linux capability, rather than running as root:

systemctl show -p User -p AmbientCapabilities -p ProtectSystem -p NoNewPrivileges --value broadcast-box.service
cap_net_bind_service broadcastbox strict yes
5

Run the round-trip self-test

The image ships a self-test that proves the access control actually works, rather than merely that a port is open. A liveness check would pass on an image that lets the whole internet publish.

The self-test authenticates with the per-VM admin token and confirms that the per-VM stream token is accepted for publishing. It then runs the negative controls that matter.

sudo bash /usr/local/lib/sac/broadcast-box-roundtrip.sh
ROUNDTRIP OK: the frontend (200) and public status API (200) serve; the per-VM admin token authenticated against /api/admin/login (200) while a missing header and a wrong token were both rejected (401); an ANONYMOUS WHIP publish was refused, an EMPTY bearer was refused 401, and a WRONG bearer was refused 401 by STREAM_PROFILE_POLICY=RESERVED, proving the ingest endpoint is not an open relay; and the per-VM reserved stream token passed WHIP authorisation (HTTP 400, not 401), proving the customer's own publish credential works. Stream key cimg-95680abd4df2fa38 is the reserved playback path.
6

Verify the auth model yourself

You do not have to take the self-test's word for it. These five requests are the whole security model, and you can run them directly.

echo "admin login, correct token: $(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Authorization: Bearer <BB_ADMIN_TOKEN>' http://127.0.0.1/api/admin/login)"
echo "admin login, wrong token: $(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Authorization: Bearer wrong' http://127.0.0.1/api/admin/login)"
echo "publish, anonymous: $(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/sdp' --data-binary 'v=0' http://127.0.0.1/api/whip)"
echo "publish, wrong bearer: $(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/sdp' -H 'Authorization: Bearer not-a-reserved-token' --data-binary 'v=0' http://127.0.0.1/api/whip)"
echo "publish, your stream token: $(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/sdp' -H 'Authorization: Bearer <BB_STREAM_TOKEN>' --data-binary 'v=0' http://127.0.0.1/api/whip)"
admin login, correct token: 200 admin login, wrong token: 401 publish, anonymous: 400 publish, wrong bearer: 401 publish, your stream token: 400

Reading those results:

The admin portal is closed. The correct token authenticates, and anything else returns 401.

Publishing is closed. An anonymous attempt is refused (400, Authorization was not set). A wrong bearer token is refused with 401 by the reserved stream policy. On a stock Broadcast Box deployment, that wrong bearer token would have been accepted and would have started a stream.

Your own token gets through authorization. It returns 400 rather than 401, because v=0 is not a negotiable WebRTC offer, so the request fails later, during media negotiation. That is exactly as it should be. The distinction between 400 and 401 is the whole point: 401 means the request was rejected at the door, and 400 means it was admitted and then given nonsense. A real encoder sends a real offer and streams.

7

Inspect your reserved stream profile

The admin API lists the reserved profiles that the server has loaded. This is the authoritative check that your publish credential is live.

curl -s -H 'Authorization: Bearer <BB_ADMIN_TOKEN>' http://127.0.0.1/api/admin/profiles echo
curl -s -o /dev/null -w 'admin status, with token: %{http_code}\n' -H 'Authorization: Bearer <BB_ADMIN_TOKEN>' http://127.0.0.1/api/admin/status
curl -s -o /dev/null -w 'admin status, no token: %{http_code}\n' http://127.0.0.1/api/admin/status
[{"streamKey":"cimg-95680abd4df2fa38","token":"****","isPublic":true,"motd":"sac Broadcast Box"}] admin status, with token: 200 admin status, no token: 401
8

Broadcast with OBS Studio

OBS Studio 30 and later speaks WHIP natively. In Settings > Stream, set:

  • Service: WHIP
  • Server: http://<your-vm-ip>/api/whip
  • Bearer Token: the broadcastbox.stream_token value from your credentials file

Choose OK, then Start Streaming. WebRTC is latency-sensitive rather than bitrate-hungry, so a moderate bitrate and a short keyframe interval give the best experience. Viewers then watch at:

http://<your-vm-ip>/<your-stream-key>

Any WHIP-capable encoder works the same way, such as ffmpeg 8 and later, GStreamer with the appropriate plugins, or a browser tab through the publish page.

You can also publish straight from the browser by opening http://<your-vm-ip>/publish/<your-stream-key>. The page captures your screen or webcam without any software installed.

9

Use the web interface

The home page lists the streams that are currently live on this instance, and it doubles as the entry point for viewers.

Opening a stream key as a path shows the player. With no broadcaster connected, the player says so plainly. Once an encoder connects, video appears in well under a second.

Switching the toggle to I want to stream turns the same panel into the publishing form. Enter your stream key and start broadcasting directly from a browser tab, capturing a screen, a window or a webcam with nothing installed.

The statistics page reports stream uptime and subscriber counts. The admin portal at /admin asks for the admin token before it shows active publishers and subscribers or lets you manage reserved stream profiles.

10

Front the server with TLS for internet exposure

Browsers restrict camera and screen capture to secure contexts, so publishing from a browser tab requires HTTPS on anything other than localhost. Playback works over plain HTTP, but you should terminate TLS for any internet-facing deployment regardless.

Point a DNS record at the VM, then place a TLS-terminating proxy in front of port 80.

Alternatively, Broadcast Box accepts SSL_CERT and SSL_KEY directly, so it can terminate TLS itself. Set them in /etc/broadcast-box/broadcast-box.env and restart the service.

Whichever route you take, UDP 3478 must remain open, because media never travels over the TLS connection.

11

Security recommendations

Keep the stream token secret. It is the publishing credential. Anyone who holds it can broadcast on your instance.

Treat the stream key as a viewing capability. Playback is deliberately open to whoever holds the key, so that links are shareable. If different audiences must not share access, create a separate reserved profile for each audience in the admin portal and revoke them individually.

Scope your Network Security Group. Restrict TCP 80 and UDP 3478 to the networks that genuinely need them, rather than the whole internet, especially while testing.

Rotate tokens from the admin portal if a credential is ever exposed. The credentials file records the values generated at first boot, and later rotations do not update it.

Leave unattended security upgrades enabled. The image is patched at build time and ships with automatic security updates armed.

The service runs as the unprivileged broadcastbox user with a strict read-only filesystem, no new privileges, and only the single capability it needs to bind port 80.

12

Support and Licensing

Broadcast Box is distributed under the MIT license. This is a repackaged open source software product with additional charges for SecureAnyCloud support services. SecureAnyCloud is not affiliated with or endorsed by Glimesh or the Broadcast Box project.

The vendored license text ships on the image at /usr/local/share/doc/broadcast-box/LICENSE.

For help with this image, contact SecureAnyCloud support at sales@secureanycloud.com. Support is available 24/7.

Deploy on Azure

Launch Broadcast Box on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure