Bb
Streaming & Messaging Azure

Broadcast Box on Ubuntu 24.04 on Azure User Guide

| Product: Broadcast Box on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Broadcast Box on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. 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, so 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 a traditional segmented streaming pipeline imposes.

You broadcast from OBS Studio or any encoder that speaks WHIP, or directly from a browser tab, and viewers watch in any modern browser with no plugin, no app and no third party streaming service in the video path.

The image is compiled from the pinned upstream release at build time and the compilers and source tree are then removed, so 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.

Security by design — publishing is closed by default. This is a deliberate departure from the upstream default and 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 belonging to a reserved stream profile. Exactly one such profile exists and it is created on this VM's first boot. An anonymous or wrong credentialled publish attempt 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.

What is included:

  • Broadcast Box (compiled from the pinned upstream release), 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, fully patched, with unattended security upgrades enabled

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in the target region

  • Subscription to the Broadcast Box listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin), TCP 80 (web interface and WHIP/WHEP signalling) and UDP 3478 (all WebRTC media)

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) 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.

Step 1: Deploy from the Azure Portal

Search Broadcast Box in Marketplace, select the cloudimg publisher, and click 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 9).

Step 2: Deploy from the Azure CLI

RG="broadcast-box-prod"; LOCATION="eastus"; VM_NAME="broadcastbox1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/broadcast-box-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --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, the player appears to connect, and no video ever arrives, because signalling succeeds over HTTP while the media plane is silently blocked. If a stream never starts, check the UDP rule first.

Step 3: First boot

On the first boot the image generates a unique admin token, stream key and publish bearer token for this VM, writes the single reserved stream profile, records the machine's public address so 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.

Step 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

The broadcast-box service reporting active under systemd, with the Broadcast Box HTTP server bound to port 80 and sshd on port 22 as the only externally bound TCP listeners and the WebRTC media mux bound on UDP port 3478, alongside the systemd hardening showing the service running as the non root broadcastbox user with only the cap_net_bind_service capability and a strict read only filesystem

Step 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, confirms the per VM stream token is accepted for publishing, and then runs the negative controls that matter.

sudo bash /usr/local/lib/cloudimg/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.

The cloudimg round trip self test printing ROUNDTRIP OK, confirming the web interface and status API serve, the per VM admin token authenticates while a missing header and a wrong token are rejected with HTTP 401, an anonymous publish and a wrong bearer token are both refused by the reserved stream policy, and the per VM stream token passes WHIP authorisation

Step 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; anything else is 401.

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

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

Five curl requests run against the running instance proving the access model: the admin login returns HTTP 200 with the per VM token and HTTP 401 with a wrong token, an anonymous publish and a publish with a wrong bearer token are both refused, and a publish with the VM's own reserved stream token passes authorisation and fails later at media negotiation instead

Step 7: Inspect your reserved stream profile

The admin API lists the reserved profiles 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":"cloudimg Broadcast Box"}]
admin status, with token: 200
admin status, no token:   401

The admin API listing the single reserved stream profile created on this VM's first boot with its stream key and public flag, followed by the admin status endpoint returning HTTP 200 when the per VM admin token is presented and HTTP 401 when it is omitted

Step 8: Broadcast with OBS Studio

OBS Studio 30 and later speaks WHIP natively. In Settings → Stream:

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

Click OK, then Start Streaming. Because WebRTC is latency sensitive rather than bitrate hungry, 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 — ffmpeg 8 and later, GStreamer with the appropriate plugins, or a browser tab via the publish page. You can also publish straight from the browser by opening http://<your-vm-ip>/publish/<your-stream-key>, which captures your screen or webcam without any software installed.

Step 9: Use the web interface

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

The Broadcast Box home page served from the instance, showing the welcome panel with the I want to watch and I want to stream toggle, a stream key entry field and a Join stream button

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

The Broadcast Box player page for this VM's reserved stream key, showing the full width player area reporting that the stream key is not currently streaming because no broadcaster has connected yet

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 Broadcast Box home panel switched to the I want to stream mode, showing the stream key entry field and a Start stream button for broadcasting directly from the browser without an external encoder

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

The Broadcast Box admin portal showing a Login panel that asks for the admin token before granting access to active publishers and subscribers and to reserved stream profile management, demonstrating that the portal is closed without the per VM token

Step 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. Broadcast Box also accepts SSL_CERT and SSL_KEY directly if you prefer it to 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 — media never travels over the TLS connection.

Step 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 per audience from the admin portal and revoke 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; it is not updated by later rotations.

  • Leave unattended security upgrades enabled. The image ships fully patched 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.

The per VM credentials file with the admin and stream tokens masked, showing the player URL, admin URL, stream key and WHIP endpoint, alongside the live proof that the admin API rejects an unauthenticated request with HTTP 401 and the publish endpoint rejects a wrong bearer token with HTTP 401

Step 12: Support and Licensing

Broadcast Box is distributed under the MIT license. This is a repackaged open source software product with additional charges for cloudimg support services. cloudimg 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 assistance with this image, contact cloudimg support, available 24/7, at support@cloudimg.co.uk.