Deployment guide

Node-Media-Server

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. 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 SecureAnyCloud image installs Node-Media-Server (verified at 2.7.4) from npm into /opt/node-media-server. It runs 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.

What you get:

  • Node-Media-Server (verified at 2.7.4) installed from npm on Node.js 20 LTS
  • node-media-server.service running as an unprivileged system user
  • RTMP ingest on port 1935, and HTTP-FLV, WebSocket-FLV and HLS playback plus the admin API on port 8000
  • node-media-server-firstboot.service for per-VM secret generation
  • A per-VM admin credential and stream-signing secret in a root-only 0600 file
  • ffmpeg and the nms-signurl / nms-selftest helper tools
  • Ubuntu 24.04 LTS base, patched at build time
  • 24/7 SecureAnyCloud support, 24h response SLA

Secure by default, with no default login. Node-Media-Server ships with no authentication, so anyone who can reach it could publish to it, play from it or read the admin API. This image does not ship in that state. Authentication is enabled, and two independent secrets are generated on each VM's first boot and written to a root-only file. One is an admin API credential (HTTP Basic auth on /api/*). The other is 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 never comes up in the insecure open mode. No two VMs share a credential, and neither secret is baked into the image.

Helper tools included. The image ships ffmpeg and two helper tools. nms-signurl generates a signed stream URL from your per-VM secret. nms-selftest runs an end-to-end check. It publishes a short test pattern, pulls it back over signed HTTP-FLV, and confirms that the admin API and the authentication rules behave correctly.

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.

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. The recommended VM size is Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and a handful of streams. For production workloads with many concurrent viewers, use Standard_D2s_v5 or larger. Live streams are relayed in real time, so plan for outbound network bandwidth rather than disk.

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
  • TCP 8000 (HTTP-FLV / HLS playback and the admin API) from the networks and players that consume the stream

In production, front port 8000 with TLS. See the HTTPS section below.

2

Deploy from the Azure CLI

RG="node-media-server-prod"; LOCATION="eastus"; VM_NAME="nms-01" IMAGE_URN="sac1647283583153:node-media-server-ubuntu-24-04:default:latest" 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
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:node-media-server-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --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
3

Connect via SSH

ssh azureuser@<vm-ip>
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.

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 private. Then 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 by root:root with mode 0600. 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. The set fills in as soon as a publisher connects.

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, which is 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 and confirms that the admin API lists the live stream. It then pulls the stream 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

A single OK line confirms that the signed publish, the signed HTTP-FLV pull and the authenticated admin API all worked, and that the unsigned and unauthenticated attempts were refused.

7

Runtime, first boot and hardening

The image runs Node.js 20 LTS and the newest 2.x line of Node-Media-Server. On first boot, a one-shot service creates the per-VM secrets and then the server starts, so the insecure open mode is never reachable. The OS is patched at build time and ships 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

The output shows Node-Media-Server 2.7.4, both services enabled, and apt-mark showhold printing nothing, which means no packages are held.

Secure the endpoint with HTTPS

Node-Media-Server serves plain HTTP on port 8000. In 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 use it as a reverse proxy for 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. Restrict port 8000 at the NSG so that 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 them, edit both NMS_API_PASS and NMS_SIGN_SECRET in the env file. Alternatively, delete the first-boot sentinel /var/lib/sac/data/node-media-server-firstboot.done and reboot to regenerate them. Then run sudo systemctl restart node-media-server. Re-sign your publish and playback URLs with the new secret.
  • Restrict the ports. Expose port 1935 only to trusted publishers and port 8000 only 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.
  • Plan for signature lifetime. Signed URLs expire. Generate fresh signatures with nms-signurl for long-lived publishers, or script the signing in your publishing pipeline using the NMS_SIGN_SECRET and the scheme md5("/<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 SecureAnyCloud image includes 24/7 support with a 24-hour response SLA. Email sales@secureanycloud.com with the offer name and your VM details. Node-Media-Server is open source under the MIT license on npm. The SecureAnyCloud charge covers packaging, security patching, image maintenance and support.

Deploy on Azure

Launch Node-Media-Server 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