Deployment guide

ErsatzTV

Overview

ErsatzTV is an open-source personal IPTV server. It turns your own local media, such as movies, shows and music videos, into live, always-on television channels that play on a schedule you define, and streams them over HLS and MPEG-TS. It emulates an HDHomeRun network tuner and publishes an M3U playlist and an XMLTV electronic programme guide, so Plex, Jellyfin, Emby and any other IPTV client can add it as a live TV source with a proper channel guide.

This SecureAnyCloud image installs the official ErsatzTV 26.6.0 self-contained linux-x64 build (recorded in /opt/ersatztv/VERSION) and runs it under systemd as the unprivileged ersatztv system user. A web interface lets you connect media libraries, build channels, arrange schedules and tune ffmpeg transcoding, and a REST API is available for automation.

ErsatzTV has no login of its own on its management interface, which would leave a bare deployment open to anyone who can reach it. This image does not ship in that state. ErsatzTV listens only on the local machine, and an nginx reverse proxy on port 80 is the only public entry point. The proxy exposes the administration interface only after a unique administrator password is generated on each VM's first boot, so there is never a window in which an unauthenticated interface is exposed to the network.

What you get:

  • ErsatzTV 26.6.0 installed from the official self-contained release, verified against the checksum published by the project before it is unpacked
  • jellyfin-ffmpeg 7.x for transcoding, the modern ffmpeg build ErsatzTV recommends, installed from an apt repository so it keeps receiving security updates and exposed on /usr/local/bin so ErsatzTV auto-detects it
  • A dedicated ersatztv systemd service, hardened with NoNewPrivileges, PrivateTmp, ProtectSystem and a writable-paths allowlist, plus automatic restarts
  • An nginx reverse proxy on port 80: the admin UI behind per-VM HTTP Basic Auth, the IPTV client surface open for your players, and a static unauthenticated /healthz
  • A host firewall (nftables) that keeps ErsatzTV's own ports reachable only from the local machine
  • A unique administrator password generated on first boot, hashed for the reverse proxy and written in plain form to the root-only file /root/ersatztv-credentials.txt, so no two deployments share a credential
  • Open tuner, playlist and guide endpoints, so Plex, Jellyfin and Emby keep working while the admin interface stays protected
  • An Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
  • 24/7 SecureAnyCloud support

Prerequisites

  • An Azure subscription and either the Azure Portal or the Azure CLI.
  • A network security group that allows inbound TCP 22 (SSH) and 80 (the ErsatzTV UI and IPTV surface). Optionally allow 443 if you add TLS for production.
  • Your own media files (movies, shows, music videos) to build channels from. You can copy them onto the VM or attach them on a data disk after launch.

Step 1 - Launch the VM

Azure Portal

  • Open the ErsatzTV on Ubuntu 24.04 LTS offer from SecureAnyCloud on the Azure Marketplace and select Get It Now, then Create.
  • Choose your subscription, resource group, region and VM size. Standard_B2s (2 vCPU, 4 GB) is a good starting size for a single software-transcoded channel. Add cores if you plan to run several concurrent channels.
  • Under Networking, allow inbound 22 and 80.
  • Select Review + create, then download or select your SSH key pair.

Azure CLI

az vm create \
  --resource-group my-ersatztv-rg \
  --name ersatztv \
  --image sac1647283583153:ersatztv-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
az vm open-port --resource-group my-ersatztv-rg --name ersatztv --port 22 --priority 1001
az vm open-port --resource-group my-ersatztv-rg --name ersatztv --port 80 --priority 1002

If prompted, accept the Marketplace image terms once per subscription with az vm image terms accept --urn sac1647283583153:ersatztv-ubuntu-24-04:default:latest.

Step 2 - Confirm the services are running

SSH in as azureuser and confirm that ErsatzTV and nginx are active. ErsatzTV listens on the loopback interface only, on port 8409 for management and port 8410 for the streaming surface. nginx is the single public entry point on port 80:

systemctl is-active ersatztv nginx
sudo ss -tlnp | grep -E ':(80|8409|8410) '

Both services report active. ErsatzTV is reachable on 127.0.0.1 only, because an nftables guard drops any off-machine access to ports 8409 and 8410. nginx listens on port 80.

Step 3 - Retrieve the per-VM administrator password

First boot generates a unique administrator password on this VM and writes it to a root-only file. Print the installed ErsatzTV version and the credentials file:

cat /opt/ersatztv/VERSION
sudo cat /root/ersatztv-credentials.txt

The file records the admin username (admin), the generated ERSATZTV_PASSWORD, the URL to open in a browser, and the unauthenticated IPTV endpoints your players use. Keep this password somewhere safe. It is not stored anywhere in the SecureAnyCloud image.

Step 4 - Confirm the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes. Query it:

curl -s http://localhost/healthz

It returns ok. This endpoint never touches ErsatzTV and never requires authentication, so it is safe to use as an Azure Load Balancer health probe.

Step 5 - Verify the authentication gate from the command line

The admin UI requires the administrator password for every request, so an unauthenticated call returns 401. Confirm this, then sign in with the per-VM password read from the root-only file:

# Secure by default: an unauthenticated request to the admin UI is rejected curl -s -o /dev/null -w 'unauthenticated admin UI -> HTTP %{http_code}\n' http://localhost/
# Read the per-VM admin password from the root-only file and authenticate
PASS=$(sudo grep '^ERSATZTV_PASSWORD=' /root/ersatztv-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' -u 'admin:wrong-password' http://localhost/
curl -s -o /dev/null -w 'per-VM password -> HTTP %{http_code}\n' -u "admin:$PASS" http://localhost/

The unauthenticated request and the wrong password both return 401. The per-VM password returns 200.

The self-test at /usr/local/bin/ersatztv-selftest checks the same round trip and is what the build verifies before capture.

Step 6 - Confirm the IPTV client surface

The endpoints your IPTV players consume, the HDHomeRun tuner, the M3U playlist and the XMLTV guide, are served without a password so Plex, Jellyfin and Emby can read them directly. The image ships one default channel, so the surface is live immediately:

# The M3U playlist and XMLTV guide, served unauthenticated curl -s http://localhost/iptv/channels.m3u | head -4 curl -s -o /dev/null -w 'xmltv.xml -> HTTP %{http_code}\n' http://localhost/iptv/xmltv.xml
# The HDHomeRun discovery endpoint (what Plex/Emby auto-detect)
curl -s -o /dev/null -w 'discover.json -> HTTP %{http_code}\n' http://localhost/discover.json

The playlist returns an #EXTM3U document, and the guide and discovery endpoints return 200. These are the same paths your players use, with this VM's public address in place of localhost.

Step 7 - Sign in to the web admin UI

Open http://<your-vm-public-ip>/ in a browser. When the browser prompts for the reverse-proxy credentials, enter admin and the ERSATZTV_PASSWORD from Step 3.

ErsatzTV opens on its home page, which shows self-diagnostic health checks and the release notes. Two health checks show an amber advisory on a fresh cloud VM. Neither is an error:

  • FFmpeg Version notes that the installed jellyfin-ffmpeg is newer than the exact release ErsatzTV pins. It works normally.
  • Hardware Acceleration notes that the default channel uses software transcoding, which is correct on a VM without a GPU.

Step 8 - Review the channels

Open Channels from the left navigation. The image ships one default channel, so the tuner, playlist and guide work the moment you launch. Build your own channels here once you have added media.

Step 9 - Add a media library

ErsatzTV builds channels from libraries of your own media. Open Media Sources, then Local Libraries, to see the library kinds (Movies, Shows, Music Videos, Songs and more).

Select Add Local Library, choose a media kind, and point it at a folder on the VM, for example a directory under /mnt where you have copied or mounted your media. ErsatzTV scans the folder, reads metadata, and makes the content available to schedule into channels.

To copy media onto the VM, use scp or rsync from your machine. Alternatively, attach and mount an Azure data disk and add the mount point as a local library. Once a library has been scanned, open Scheduling to arrange the content into a channel's programme. The channel then appears in your M3U playlist and XMLTV guide automatically.

Step 10 - Build a schedule

Open Schedules from the left navigation and select Add Schedule to define what plays on a channel and when.

A schedule references collections of your media and lays them out into a continuous programme that ErsatzTV streams as a live channel.

Step 11 - Connect Plex, Jellyfin or Emby

Point your IPTV client at this VM using its public address. The endpoints are unauthenticated, so your player can read them directly:

  • HDHomeRun tuner / device: http://<your-vm-public-ip>/. Plex and Emby auto-detect the tuner through discover.json, lineup.json and device.xml.
  • M3U playlist: http://<your-vm-public-ip>/iptv/channels.m3u
  • XMLTV guide: http://<your-vm-public-ip>/iptv/xmltv.xml

In Plex, add ErsatzTV under Live TV & DVR. Plex detects the HDHomeRun-style tuner and imports the XMLTV guide.

In Jellyfin or Emby, add a tuner device of type M3U that points at the playlist URL. Then add an XMLTV guide source that points at the guide URL.

Security and networking notes

  • Open only ports 22 and 80. Never open ports 8409 or 8410 in your network security group. ErsatzTV binds them to the local machine and a host firewall drops any off-machine access, but keeping them closed at the network layer is the correct posture. All access goes through nginx on port 80.
  • The admin UI is gated; the IPTV surface is not. The reverse proxy requires the per-VM password for the management interface, but it intentionally leaves the tuner, playlist and guide open so your players work. If your VM is reachable from the public internet, anyone who knows the address can read your channel list and streams. Use the network security group to restrict inbound port 80 to the networks that need it, such as your player and your home network.
  • Rotate or change the password by regenerating a bcrypt entry with sudo htpasswd -B /etc/nginx/.ersatztv.htpasswd admin. Then update /root/ersatztv-credentials.txt to match.

Production: add a domain and TLS

For a production deployment, put a TLS certificate in front of nginx. Point a DNS name at the VM and open port 443. Then install certbot and the nginx plugin, and run sudo certbot --nginx -d your-domain to obtain and install a certificate.

ErsatzTV honors the forwarded protocol and host headers, so the M3U and XMLTV URLs it generates automatically use your HTTPS domain.

Troubleshooting

  • ErsatzTV service status: systemctl status ersatztv and sudo journalctl -u ersatztv -n 100 --no-pager
  • First-boot log (password generation and the auth-gate proof): sudo cat /var/log/ersatztv-firstboot.log
  • The admin UI returns 502 briefly after a reboot: ErsatzTV runs database migrations on start, and the interface is ready within a minute. The health endpoint and IPTV surface come up once migrations finish.
  • ffmpeg version: confirm the transcoder with ffmpeg -version | head -1. It reports a 7.x jellyfin build.

Support

Every SecureAnyCloud image is backed by 24/7 support. If you have questions about this ErsatzTV image, contact the SecureAnyCloud team through the Azure Marketplace listing.

This is a repackaged open-source software product, with additional charges for SecureAnyCloud support services. ErsatzTV is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders, and their use does not imply any affiliation with or endorsement by them.

Deploy on Azure

Launch ErsatzTV 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