MistServer 3.13 on AWS User Guide
Overview
This guide covers launching and using the MistServer 3.13 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg media servers listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
MistServer is an open source streaming media server. An encoder such as OBS Studio or ffmpeg publishes a live stream to it over RTMP, and MistServer repackages that stream on the fly for every kind of viewer: HLS for browsers and phones, CMAF (MPEG-DASH and fragmented MP4), progressive MPEG-TS, MP4 and FLV, plus a built in web player. It can also pull streams from other servers and record or re-push them.
What cloudimg adds:
- Built from the upstream 3.13 source. MistServer is compiled by cloudimg from the checksum verified MistServer 3.13 release tag (commit
ddd9bec7). The upstream code is unmodified; the one change is the dependency pin, which is raised from Mbed TLS 3.6.6 to the current Mbed TLS 3.6.7 security release. The exact diff ships on the image. - A per-instance admin login, created before any port opens. First boot generates the administrator password and the RTMP publishing passphrase for your instance and writes them to
/root/mistserver-credentials.txt. MistServer and nginx refuse to start until that has happened, so there is never a window in which the first visitor could claim the server. MistServer allows only that one account. - HTTPS only management. The management interface and API are served by nginx on port 443 with a certificate generated on your instance's first boot, behind the admin login. Port 80 redirects to HTTPS and answers
/healthzfor load balancers. The MistServer controller itself listens only on127.0.0.1:4242. - Publishing protected by a passphrase. The ready made
livestream accepts an RTMP push only when the publisher supplies your instance's passphrase. - A sandboxed service that cannot reach inside your network. MistServer runs as its own unprivileged account in a strict systemd sandbox. A firewall table loaded at every boot stops that account from opening connections to the EC2 instance metadata service, to private and reserved address ranges, and to your VPC's own IPv4 and IPv6 ranges, so a stream source URL cannot be used to reach your internal services. Public sources work as usual.
- No SRT or RIST, no ffmpeg. SRT and RIST support are not compiled in. The image ships no ffmpeg; MistServer packages streams itself.
- Fully patched Ubuntu 24.04 LTS at build time, IMDSv2 required, with unattended security upgrades switched on.
What is on the image: MistServer 3.13 in /opt/mistserver (compiled by cloudimg, with its bundled Mbed TLS 3.6.7, libsrtp 2.6.0 and usrsctp 0.9.5.0 libraries in /opt/mistserver/lib), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet.
- A security group that allows:
- TCP 22 (SSH) from your own networks only
- TCP 443 (management interface and API over HTTPS) from your administrators' networks
- TCP 80 (redirect to HTTPS and
/healthz) from the same sources as 443 - TCP 1935 (RTMP publishing) from your encoders
- TCP 8080 (playback: HLS, CMAF, MPEG-TS, MP4, FLV and the web player) from your viewers or your CDN, usually anywhere
Streaming is mostly network bound: MistServer repackages streams without transcoding, so an m5.large serves many concurrent viewers of a few streams. Put a CDN such as Amazon CloudFront in front of port 8080 for large audiences.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick MistServer 3.13 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=mistserver}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| MistServer 3.13 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot generates the certificate, the admin password and the publishing passphrase, then starts MistServer and nginx:
systemctl is-active mistserver-egress-guard mistserver nginx
sudo test -f /var/lib/cloudimg/mistserver-firstboot.done && echo FIRSTBOOT_DONE
/opt/mistserver/bin/MistController -v 2>&1 | head -1
All three units are active, the first boot marker is present and MistServer reports its version:
active
active
active
FIRSTBOOT_DONE
Version: 3.13, release Generic_x86_64
nginx serves the management interface on 80 and 443; MistServer publishes RTMP on 1935 and playback on 8080, and its controller listens on loopback only:
sudo ss -lntpH | awk '{split($6, a, "\""); print $4, a[2]}' | sort -u
*:1935 MistOutRTMP
*:8080 MistOutHTTP
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
127.0.0.1:4242 MistController
127.0.0.53%lo:53 systemd-resolve
127.0.0.54:53 systemd-resolve
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx
Step 4: Get your login and publishing details
sudo grep -E '^MIST_(HOST|URL|ADMIN_USER|HLS_URL|TS_URL|PLAYER_URL)=' /root/mistserver-credentials.txt
sudo stat -c '%a %U %n' /root/mistserver-credentials.txt
MIST_HOST=ec2-203-0-113-10.compute-1.amazonaws.com
MIST_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
MIST_ADMIN_USER=admin
MIST_HLS_URL=http://ec2-203-0-113-10.compute-1.amazonaws.com:8080/hls/live/index.m3u8
MIST_TS_URL=http://ec2-203-0-113-10.compute-1.amazonaws.com:8080/live.ts
MIST_PLAYER_URL=http://ec2-203-0-113-10.compute-1.amazonaws.com:8080/live.html
600 root /root/mistserver-credentials.txt
MIST_HOST is the instance's public DNS name from EC2 instance metadata; the certificate also names the public and private IP addresses. To use your own DNS name, see Step 9.
The full file, which also holds MIST_ADMIN_PASSWORD and MIST_PUSH_PASSPHRASE, is readable by root only. Show it when you need the values:
sudo cat /root/mistserver-credentials.txt
Keep the password and the passphrase secret; anyone with the passphrase can publish to your live stream.
Step 5: Open the management interface
Browse to the MIST_URL from Step 4 (https://<public-ip>/) and accept the self signed certificate warning (Step 9 shows how to use your own name and certificate). Your browser asks for a user name and password: enter admin and your MIST_ADMIN_PASSWORD. The MistServer interface then shows its own login; enter the same user name and password.

The Overview page shows the MistServer version, the configured and active streams, the current connections and the enabled protocols:

Step 6: Publish a stream and play it back
The image comes with a stream named live that accepts an RTMP push carrying your passphrase. In OBS Studio, open Settings > Stream, choose Custom, and enter:
- Server:
rtmp://<public-ip>:1935/<passphrase>(yourMIST_PUSH_PASSPHRASE) - Stream Key:
live
With ffmpeg on your own computer, the same publish is:
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -c:a aac -f flv "rtmp://<public-ip>:1935/<passphrase>/live"
A publish with a missing or wrong passphrase is refused. While you are publishing, viewers can play the stream on port 8080:
- Web player page:
http://<public-ip>:8080/live.html - HLS (browsers, iOS, Android, smart TVs):
http://<public-ip>:8080/hls/live/index.m3u8 - CMAF / MPEG-DASH:
http://<public-ip>:8080/cmaf/live/index.mpd - MPEG-TS:
http://<public-ip>:8080/live.ts - MP4 / FLV:
http://<public-ip>:8080/live.mp4,http://<public-ip>:8080/live.flv
The Streams page lists live with its state, viewers and inputs:

The image includes a self test that does all of this on the instance itself: it checks that anonymous and wrong password requests are refused and that a second account cannot be created, logs in, publishes a short silent test stream to live over RTMP with a wrong passphrase (refused) and then with yours (accepted), and plays it back as MPEG-TS and HLS:
sudo mist-selftest
The last line sums up every check:
OK MistServer self-test passed: HTTPS front 401 anonymous / wrong password, API CHALL without login, second account refused, admin login OK, wrong-passphrase RTMP publish refused, passphrase publish accepted, MPEG-TS (65536 bytes) + HLS pulled back from :8080
The test uses a temporary stream that it removes when it finishes; your live stream configuration is left as it was. Run it when no encoder is publishing to live.
Step 7: Protocols and the API
The Protocols page lists the enabled inputs and outputs: RTMP on 1935 and the HTTP output on 8080, which carries HLS, CMAF, MPEG-TS, MP4, FLV and the web player.

To script MistServer from the instance, use the bundled mist-api helper. It logs in over the local HTTPS front with your admin credentials and prints MistServer's JSON reply. The full reply includes the stream configuration, and with it your publishing passphrase, so this example prints only the fields it needs: whether the login was accepted, the streams being published right now and the enabled protocols:
sudo mist-api '{"active_streams":true}' | python3 -c 'import json, sys; d = json.load(sys.stdin); print("api:", d["authorize"]["status"]); print("active_streams:", d.get("active_streams")); print("protocols:", ", ".join(sorted(p["connector"] for p in d["config"]["protocols"])))'
With nothing publishing, the output is:
api: OK
active_streams: None
protocols: CMAF, FLV, HLS, HTTP, HTTPTS, MP4, RTMP
While an encoder publishes to live, active_streams lists ['live']. The API commands are described in the MistServer documentation.
WebRTC and RTSP are not enabled on this image, and the listing's security group opens no UDP ports. To add one, enable the protocol on the Protocols page (WebRTC needs a UDP port range and your instance's public IP set as its external address; RTSP uses TCP 554 by default), then open the same ports in your security group. Publishing over SRT and RIST is not available: they are not compiled into this build.
Step 8: Pull a stream from another server
MistServer can use a remote stream as a source: on the Streams page create a stream and set its source to, for example, rtmp://origin.example.com/app/stream or https://origin.example.com/live/index.m3u8. MistServer connects to the source when the first viewer arrives.
Two things to know:
- Sources must be public. The firewall table described in the Overview refuses connections from MistServer to the instance metadata service, to private and reserved address ranges and to your VPC, so a source inside your VPC will not connect. If you need to pull from a private address, see Maintenance.
- Outbound TLS is not verified. When MistServer pulls from an
https://orrtmps://source it does not verify the remote server's certificate (MistServer sets Mbed TLS toVERIFY_NONEfor outbound connections). The connection is encrypted but not authenticated, so pull only from sources you trust, over networks you trust. Inbound administration is not affected: it goes only through the nginx HTTPS front, with its own certificate.
Step 9: Use your own DNS name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address stable), then tell the instance its name. This regenerates the self signed certificate for that name, updates the credentials file and reloads nginx:
sudo mistserver-set-hostname stream.your-domain.example
To use a certificate from a public CA instead, replace /etc/ssl/mistserver/mistserver.crt (full chain) and /etc/ssl/mistserver/mistserver.key (mode 0600) and run sudo systemctl reload nginx. Playback on port 8080 is plain HTTP; for HTTPS playback, put Amazon CloudFront or an Application Load Balancer with an AWS Certificate Manager certificate in front of port 8080.
Step 10: Change the password and passphrase
To replace the admin password and the publishing passphrase (for example after a staff change), run the rotation helper. It writes new values to MistServer, nginx and the credentials file; update your encoders with the new passphrase:
sudo mistserver-rotate-credentials
Step 11: Security checks
These checks show the protections described in the Overview on your own instance. First, the management front: an anonymous request gets the login challenge, plain HTTP redirects to HTTPS and the MistServer controller cannot be reached on the instance's network address:
H=$(sudo sed -n 's/^MIST_HOST=//p' /root/mistserver-credentials.txt)
curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w 'https://<host>/ without a login -> HTTP %{http_code}\n' "https://$H/"
curl -s --resolve "$H:80:127.0.0.1" -o /dev/null -w 'http://<host>/ -> HTTP %{http_code}\n' "http://$H/"
curl -s -m 5 -o /dev/null -w 'MistServer controller on the private address -> %{http_code}\n' "http://$(hostname -I | awk '{print $1}'):4242/" || true
https://<host>/ without a login -> HTTP 401
http://<host>/ -> HTTP 301
MistServer controller on the private address -> 000
000 means the connection was refused. Next, the egress sandbox: the mistserver account cannot reach the instance metadata service or the instance's own private address, and the firewall table and the systemd address filter are in place:
sudo -u mistserver curl -s -m 5 -o /dev/null -w 'uid mistserver -> instance metadata: %{http_code}\n' http://169.254.169.254/latest/meta-data/ || true
sudo -u mistserver curl -s -m 5 -o /dev/null -w "uid mistserver -> this instance's private address: %{http_code}\n" "http://$(hostname -I | awk '{print $1}')/healthz" || true
sudo nft list table inet cloudimg_mist_egress | grep -oE 'comment "cloudimg-[a-z0-9-]+"' | sort -u
systemctl show mistserver nginx -p IPAddressDeny --value
uid mistserver -> instance metadata: 000
uid mistserver -> this instance's private address: 000
comment "cloudimg-api-guard"
comment "cloudimg-api-guard-udp"
comment "cloudimg-api-guard6"
comment "cloudimg-api-guard6-udp"
comment "cloudimg-metadata"
comment "cloudimg-metadata6"
comment "cloudimg-multicast"
comment "cloudimg-multicast6"
comment "cloudimg-private"
comment "cloudimg-private6"
comment "cloudimg-vpc"
comment "cloudimg-vpc6"
168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
The api-guard rules let only root, nginx and MistServer's own processes reach the controller port on loopback, so other local accounts cannot use the API; the vpc rules hold your VPC's IPv4 and IPv6 ranges, read from instance metadata at every boot. Finally, the build: the Mbed TLS library MistServer loads is 3.6.7, no SRT or RIST binaries are present, MistServer runs as its own account in a strict sandbox, and Ubuntu's shared snakeoil certificate service is masked:
python3 -c 'import ctypes; b = ctypes.create_string_buffer(32); ctypes.CDLL("/opt/mistserver/lib/libmbedcrypto.so.16").mbedtls_version_get_string_full(b); print(b.value.decode())'
ls /opt/mistserver/bin | grep -ciE 'srt|rist' || true
systemctl show mistserver -p User -p ProtectSystem -p NoNewPrivileges --value
systemctl is-enabled ssl-cert.service || true
Mbed TLS 3.6.7
0
mistserver
strict
yes
masked
Maintenance
- Configuration: MistServer keeps its configuration in
/var/lib/mistserver/mistserver.confand saves changes you make in the interface there. Keep the controller on127.0.0.1:4242: nginx publishes it, and a guard refuses to start MistServer if the controller is moved to a public address, a second account appears or the firewall table is missing. - Logs:
journalctl -u mistserverfor MistServer,/var/log/nginx/for the management front. - Health checks:
GET /healthzon port 80 or 443 returnsokwithout a login. - Private sources: the egress rules are in
/usr/local/sbin/mistserver-egress-applyand are loaded bymistserver-egress-guard.serviceat every boot. Allowing a private source means editing that script; keep the metadata rules. - Security updates: unattended security upgrades are on and cover nginx and every system library MistServer uses. Reboot when a new kernel is installed. MistServer and its bundled Mbed TLS, libsrtp and usrsctp libraries are updated by a new version of this image.
Licences and source code
MistServer is released into the public domain under the Unlicense. It is built from the upstream 3.13 source; the only change is the Mbed TLS dependency pin, and that diff is in /usr/share/doc/mistserver-cloudimg/mbedtls-pin.diff. The bundled Mbed TLS 3.6.7 is licensed under the Apache License 2.0, libsrtp 2.6.0 and usrsctp 0.9.5.0 under the BSD 3-Clause licence, and the compiled in quirc library under the ISC licence. Every licence is in /usr/share/doc/mistserver-cloudimg/, with build details in README.cloudimg.
Support
cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. This image is provided by cloudimg and is not affiliated with or endorsed by DDVTech or the MistServer project.