Node-Media-Server on Ubuntu 24.04 on Azure User Guide
Overview
Node-Media-Server is a lightweight open source live media streaming server written in Node.js. A publisher such as OBS Studio, ffmpeg, a hardware encoder or a mobile app pushes a live RTMP feed to a named stream, and the server republishes that stream in real time as HTTP-FLV, WebSocket-FLV and HLS for low latency playback in browsers and native players. A JSON admin API reports the live streams and session statistics.
The cloudimg image installs Node-Media-Server (verified at 2.7.4) from npm into /opt/node-media-server, running as the unprivileged node-media-server service on Node.js 20 LTS. RTMP ingest listens on port 1935 and HTTP playback plus the admin API listen on port 8000.
Secure by default, no default login: Node-Media-Server ships with no authentication at all, which means anyone who can reach it could publish to it, play from it or read the admin API. This image never ships that. Authentication is enabled and two independent secrets are generated on each VM's first boot, written to a root only file: an admin API credential (HTTP Basic auth on /api/*) and a stream-signing secret that every publish and playback URL must be signed with. The server refuses to start unless both secrets are present, so an instance can never come up in the insecure open mode, and no two VMs share a credential. Neither secret is baked into the image.
Batteries included: the image ships ffmpeg and two helper tools: nms-signurl generates a signed stream URL from your per-VM secret, and nms-selftest runs an end to end check by publishing a short test pattern, pulling it back over signed HTTP-FLV, and confirming the admin API and the authentication rules all behave.
Note on the interface: Node-Media-Server is a headless, API-first service with no web UI. You drive it with an RTMP publisher (OBS, ffmpeg), an HTTP-FLV or HLS player, and the admin REST API. This guide uses ffmpeg and curl.
What is included:
- Node-Media-Server (verified at 2.7.4) installed from npm on Node.js 20 LTS
node-media-server.servicerunning as an unprivileged system user- RTMP ingest on port 1935; HTTP-FLV, WebSocket-FLV and HLS playback plus the admin API on port 8000
node-media-server-firstboot.servicefor the per-VM secret generation- A per-VM admin credential and stream-signing secret in a root only
0600file - ffmpeg and the
nms-signurl/nms-selftesthelper tools - Ubuntu 24.04 LTS base, fully patched
- 24/7 cloudimg support, 24h response SLA
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet with a subnet. Node-Media-Server is lightweight, but sustained throughput scales with the number of concurrent publishers and viewers and with the bitrate of each stream. Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and a handful of streams, or a Standard_D2s_v5 or larger for production workloads with many concurrent viewers. Live streams are relayed in real time, so plan for outbound network bandwidth rather than disk.
Step 1: Deploy from the Azure Portal
Search the Marketplace for Node-Media-Server on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network, TCP 1935 (RTMP ingest) from your publishers, and TCP 8000 (HTTP-FLV / HLS playback and the admin API) from the networks and players that consume the stream. Front port 8000 with TLS in production (see the HTTPS section below).
Step 2: Deploy from the Azure CLI
RG="node-media-server-prod"; LOCATION="eastus"; VM_NAME="nms-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/node-media-server-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name nms-vnet --address-prefix 10.90.0.0/16 --subnet-name nms-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name nms-nsg
az network nsg rule create -g "$RG" --nsg-name nms-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name nms-nsg --name allow-rtmp --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 1935 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name nms-nsg --name allow-http --priority 120 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 8000 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name nms-vnet --subnet nms-subnet --nsg nms-nsg --public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
Step 4: Verify the service is running
Node-Media-Server listens on port 1935 for RTMP ingest and port 8000 for HTTP playback and the admin API. The health endpoint is public; the admin API and streaming require your credentials.
sudo systemctl start node-media-server
systemctl is-active node-media-server
ss -tln | grep -E ':1935 |:8000 '
curl -s http://127.0.0.1:8000/health
You should see the service active, both listening sockets, and ok from the health endpoint.

Step 5: Read your per-VM secrets
Two secrets were generated on this VM's first boot and stored in a root only file: the admin API credential and the stream-signing secret. Read them and keep them secret. Confirm that unauthenticated admin requests are rejected.
sudo cat /root/node-media-server-credentials.txt
echo "unauthenticated /api/streams -> HTTP $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8000/api/streams)"
The note holds NMS_API_USER, NMS_API_PASS and NMS_SIGN_SECRET, is owned root:root with mode 0600, and an unauthenticated call to /api/streams returns 401 because the admin API requires the credential.
Query the admin API with your per-VM credential:
NMS_USER=$(sudo grep '^NMS_API_USER=' /root/node-media-server-credentials.txt | cut -d= -f2-)
NMS_PASS=$(sudo grep '^NMS_API_PASS=' /root/node-media-server-credentials.txt | cut -d= -f2-)
curl -s -u "$NMS_USER:$NMS_PASS" http://127.0.0.1:8000/api/streams | jq .
With no live streams yet, the admin API returns an empty live set. It fills in as soon as a publisher connects.

Step 6: Publish a stream and play it back
Every publish and playback URL must carry a sign query parameter derived from your per-VM signing secret. The nms-signurl helper generates one for a stream path (the default app is live). A signature is valid until it expires (one hour by default).
sudo nms-signurl /live/mystream
This prints sign=<expiry>-<hash>. Append it to your publish and playback URLs. Publish from ffmpeg (or point OBS at rtmp://<vm-ip>:1935/live with the stream key mystream?sign=<expiry>-<hash>), then play the same signed stream over HTTP-FLV in VLC, ffplay, or a browser flv.js player:
# Publish a file or a live source over the SIGNED RTMP URL:
ffmpeg -re -i input.mp4 -c copy -f flv "rtmp://<vm-ip>:1935/live/mystream?sign=<expiry>-<hash>"
# Play it back over SIGNED HTTP-FLV (VLC / ffplay / flv.js in a browser):
ffplay "http://<vm-ip>:8000/live/mystream.flv?sign=<expiry>-<hash>"
To verify the whole pipeline end to end at any time, run the bundled self-test. It publishes a short test pattern over a signed RTMP URL, confirms the admin API lists the live stream, pulls it back over signed HTTP-FLV, and proves that an unsigned publish, an unsigned play and an unauthenticated admin request are all rejected:
sudo nms-selftest
You should see a single OK line confirming the signed publish, the signed HTTP-FLV pull and the authenticated admin API all worked, and that the unsigned and unauthenticated attempts were refused.

Step 7: Runtime, first boot and hardening
The image runs a current Node.js 20 LTS and the newest 2.x line of Node-Media-Server. On first boot a one shot service mints the per-VM secrets and then the server starts, so the insecure open mode is never reachable. The OS is fully patched with unattended security upgrades enabled.
node -e "console.log('Node-Media-Server ' + require('/opt/node-media-server/node_modules/node-media-server/package.json').version)"
systemctl is-enabled node-media-server node-media-server-firstboot
apt-mark showhold
You should see Node-Media-Server 2.7.4, both services enabled, and apt-mark showhold printing nothing (no held packages).

Secure the endpoint with HTTPS
Node-Media-Server serves plain HTTP on port 8000. For production, terminate TLS in front of it so playback and the admin API travel over HTTPS. Terminate at an Azure load balancer or Application Gateway, or install nginx with a certificate from Let's Encrypt and reverse proxy 127.0.0.1:8000:
sudo apt-get update && sudo apt-get install -y nginx
sudo certbot --nginx -d <your-domain>
Point your players at https://<your-domain>/live/<stream>.flv?sign=... once TLS is in place, and restrict port 8000 at the NSG so only the proxy reaches the origin.
Security and operations notes
- Rotate the secrets: the admin credential and signing secret live in
/etc/node-media-server/nms.env(loaded by systemd) and are recorded in/root/node-media-server-credentials.txt. To rotate, edit bothNMS_API_PASSandNMS_SIGN_SECRETin the env file (or delete the first-boot sentinel/var/lib/cloudimg/node-media-server-firstboot.doneand reboot to regenerate), thensudo systemctl restart node-media-server. Re-sign your publish and playback URLs with the new secret. - Restrict the ports: only expose port 1935 to trusted publishers and port 8000 to your players or CDN. The admin API on
/api/*is always behind HTTP Basic auth, but keep it off the public internet where possible. - Signature lifetime: signed URLs expire. Generate fresh signatures with
nms-signurlfor long-lived publishers, or script the signing in your publishing pipeline using theNMS_SIGN_SECRETand the schememd5("/<app>/<stream>-<expiry>-<secret>"). - Scale out: for many concurrent viewers, put a CDN or an nginx cache in front of the HTTP-FLV / HLS endpoint, or run several instances behind a load balancer.
Support
Every cloudimg image includes 24/7 support with a 24-hour response SLA. Email support@cloudimg.co.uk with the offer name and your VM details. Node-Media-Server is open source under the MIT license on npm; the cloudimg charge covers packaging, security patching, image maintenance and support.