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.servicerunning 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.servicefor 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, 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.
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.
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 StandardConnect via SSH
ssh azureuser@<vm-ip>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/healthYou should see the service active, both listening sockets, and ok from the health endpoint.
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.
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/mystreamThis 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-selftestA 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.
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 showholdThe 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 bothNMS_API_PASSandNMS_SIGN_SECRETin the env file. Alternatively, delete the first-boot sentinel/var/lib/sac/data/node-media-server-firstboot.doneand reboot to regenerate them. Then runsudo 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-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 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
Overview
This image runs Node-Media-Server, a lightweight open source live media streaming server written in Node.js. It ingests an RTMP feed from any publisher, such as OBS Studio, ffmpeg, a hardware encoder or a mobile app, and republishes that feed in real time as HTTP-FLV, WebSocket-FLV and HLS for low latency playback in browsers and native players. A JSON admin API reports live stream and session statistics.
The stable open source 2.x line is shipped here (version 2.7.4), which is MIT licensed and freely redistributable. The server is installed from npm into /opt/node-media-server on a Node.js 20 runtime and runs as the unprivileged nms system user under systemd, so it restarts automatically and logs cleanly through the journal.
RTMP ingest listens on port 1935. HTTP-FLV, WebSocket-FLV, HLS playback and the admin API all listen on port 8000.
Secure by default
Upstream Node-Media-Server ships with no authentication at all: anyone who can reach it could publish to your server, play from it, or read the admin API. This image does not ship that way. It enables authentication for the admin API, for publishing and for playback, and generates two independent secrets on the first boot of every deployed instance:
- An
adminAPI credential (HTTP Basic auth, user admin) that protects the statistics API at/api/*.
- A stream signing secret that every publish and playback URL must be signed with.
Both secrets are unique to each instance, so two instances launched from the same AMI never share them, and neither is baked into the image. They are written to /root/node-media-server-credentials.txt with mode 0600, readable only by root. The server refuses to start if either secret is missing or too short, so the image can never boot into the insecure open mode.
Prerequisites
Before you deploy this image you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network, and ports 1935 and 8000 from the networks that will publish and play streams
- The AWS CLI (version 2) installed locally if you plan to deploy from the command line, and ffmpeg on any machine you intend to publish from
Connecting to your instance
Connect over SSH as the default login user for your operating system variant. The recommended instance type is m5.large or larger.
OS variant SSH login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
Replace <instance-public-ip> with the public IPv4 address of your instance and your-key.pem with your EC2 private key.
Launching from the AWS Marketplace
- Open the product page in the AWS Marketplace and choose Continue to Subscribe, then Continue to Configuration.
- Select the software version and your Region, then Continue to Launch.
- Choose an instance type of
m5.largeor larger, select your VPC, subnet and key pair, and attach a security group that allows inbound 22, 1935 and 8000 as described above.
- Launch the instance and wait for the status checks to pass.
Launching from the AWS CLI
# from your workstation aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name your-key \
--security-group-ids <sg-id> \
--subnet-id <subnet-id> \
--count 1The <ami-id> is shown on the Marketplace launch page for your Region.
Retrieving your per-instance secrets
Every instance generates its own admin credential and stream signing secret on first boot and writes them to a root only file, along with the ready to use endpoint URLs. SSH in and read it:
sudo cat /root/node-media-server-credentials.txtYou will see the admin user and password, the signing secret, and the RTMP, HTTP and admin API URLs already filled in with the instance public address. Keep this file secret.
Verifying the service
Confirm the service is running and both ports are listening:
systemctl is-active node-media-serverss -tlnp | grep -E ':1935|:8000'The unauthenticated health endpoint returns a small static response and is handy for load balancer health checks:
curl -fsS http://127.0.0.1:8000/healthThe authenticated admin API
The admin API reports live streams and sessions as JSON. It is protected: an unauthenticated request is refused with HTTP 401.
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/api/streamsSupply the per instance admin credential and it returns the live stream statistics as JSON. Read the password straight from your root only credentials file so it never appears on the command line history:
API_PASS=$(sudo grep -m1 '^NMS_API_PASS=' /root/node-media-server-credentials.txt | cut -d= -f2-)
curl -s -u "admin:${API_PASS}" http://127.0.0.1:8000/api/streams | jq .Signing a stream URL
Because publishing and playback are authenticated, every publish and play URL must carry a signature derived from your instance signing secret. The bundled nms-signurl helper computes it for a stream path. To publish or play the stream key mystream under the default live application:
sudo nms-signurl /live/mystreamThis prints a sign=<expire>-<md5> query string. Append it to the RTMP publish URL or the HTTP-FLV or HLS playback URL. Signatures are time limited (one hour by default), so generate a fresh one when you start a new session.
Publishing a stream
Publish an RTMP feed to rtmp://<instance-public-ip>:1935/live/mystream, appending the signature. From any machine with ffmpeg installed:
# from your workstation: publish a test pattern to the server SIGN="sign=<expire>-<md5>"
# from:
sudo nms-signurl /live/mystream ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 \
-c:v libx264 -preset veryfast -tune zerolatency -f flv \
"rtmp://<instance-public-ip>:1935/live/mystream?${SIGN}"To publish from OBS Studio, set the stream Server to rtmp://<instance-public-ip>:1935/live and the Stream Key to mystream?sign=<expire>-<md5> using a signature from nms-signurl. See the OBS project documentation at obsproject.com for encoder settings.
Playing back a stream
While a stream is publishing, play it back over signed HTTP-FLV or HLS on port 8000. Generate a fresh signature for playback with sudo nms-signurl /live/mystream, then:
- HTTP-FLV:
http://<instance-public-ip>:8000/live/mystream.flv?sign=<expire>-<md5>
- HLS:
http://<instance-public-ip>:8000/live/mystream/index.m3u8?sign=<expire>-<md5>
Open either URL in a compatible player such as ffplay, VLC, or an flv.js or hls.js web player. The following retrieves the first bytes of a signed HTTP-FLV stream and confirms the FLV header:
# from your workstation, while a stream is publishing curl -s "http://<instance-public-ip>:8000/live/mystream.flv?sign=<expire>-<md5>" | head -c 3End-to-end self test
The image bakes a self test that exercises the whole pipeline: it publishes a short signed test pattern, pulls it back over signed HTTP-FLV, queries the admin API, and proves that an unauthenticated admin request, an unsigned publish and an unsigned play are all rejected. Run it any time to confirm the deployment is healthy:
sudo nms-selftestIt prints an OK line on success or a FAIL: line describing the first failing check.
Enabling HTTPS and a custom domain
For public deployments, terminate TLS in front of Node-Media-Server with a reverse proxy such as nginx or a load balancer, and front RTMP with your own edge if required. A common pattern is to obtain a certificate with certbot for your-domain.example.com and proxy https://your-domain.example.com/ to http://127.0.0.1:8000/. SecureAnyCloud support can provide a worked reverse proxy and TLS configuration for your environment.
Maintenance
- Service control: manage the server with
systemctl(node-media-server). Logs are in the journal:journalctl -u node-media-server.
- Configuration: the runtime configuration lives in
/opt/node-media-server/app.js; the per instance secrets are in the systemd EnvironmentFile/etc/node-media-server/nms.env(0640 root:root).
- Rotating secrets: to force fresh per instance secrets, remove
/var/lib/sac/data/node-media-server-firstboot.doneand restart thenode-media-server-firstbootservice, then restart the server.
- OS updates: apply operating system security updates through your normal patch process (
apt-get updateand upgrade on Ubuntu).
Support
Every deployment is backed by 24/7 SecureAnyCloud support via email and live chat at sales@secureanycloud.com. Support covers deployment and instance sizing, publisher and player integration, URL signing and authentication, TLS termination and custom domains, reverse proxy and CDN fronting, and version upgrades within the stable line. Include your instance ID and AWS Region to expedite troubleshooting.
Deploy on AWS
Launch Node-Media-Server on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com