Media & Entertainment AWS

ErsatzTV 26.11.6 on AWS User Guide

| Product: ErsatzTV 26.11.6 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and verifying ErsatzTV 26.11.6 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened SRS Streaming Media Server & Media Servers AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other media and streaming products in the listing are described on the listing's guide hub.

ErsatzTV is a free, self-hosted server (Zlib licence) that turns the films, shows, music videos and other media you own into live, scheduled TV channels. You build channels and schedules in its web interface; ErsatzTV then plays them out around the clock as an IPTV playlist (M3U) with a programme guide (XMLTV), and as an emulated HDHomeRun tuner, so Plex, Jellyfin, Emby, VLC, TiviMate and other IPTV players can watch them like broadcast TV. Media can come from local folders or from Plex, Jellyfin and Emby servers you connect.

What is on the image:

  • ErsatzTV 26.11.6, the upstream self-contained Linux release (the .NET 10.0.14 runtime is inside it), checked against a pinned SHA-256, in /opt/ersatztv
  • The ErsatzTV "Next" streaming engine (ersatztv-channel 0.2.4), rebuilt by cloudimg from the same upstream source with newer patch releases of its TLS and networking libraries
  • FFmpeg 8.1.3 (the upstream static GPL build, no non-free codecs) as the transcoder, at /usr/local/bin/ffmpeg and /usr/local/bin/ffprobe
  • ErsatzTV listening only behind nginx serving HTTPS on port 443 with a certificate created on your instance; port 80 redirects to HTTPS
  • A per-instance administrator login and a per-instance secret IPTV address, both created on first boot
  • ersatztv-set-hostname (serve ErsatzTV under your own DNS name), ersatztv-iptv-url (show or replace the IPTV address) and ersatztv-selftest (an end-to-end check)

Default user: ubuntu · Architecture: x86_64 · Root volume: 16 GiB gp3

Security posture

  • No open default. ErsatzTV has no accounts of its own: upstream, anyone who can reach its port controls it. On this image nginx puts a sign-in in front of every page, the REST API and the standard IPTV and HDHomeRun addresses. The only login is the administrator admin, whose 32-character password is generated on your instance at first boot and stored by nginx only as a bcrypt hash.
  • A secret address for players that cannot sign in. Many IPTV players cannot send a user name and password, so the playlist, guide, streams and HDHomeRun endpoints are also served under https://<host>/tv-<token>/, where the token is 128 random bits created on your instance. Nothing else is reachable under it (any other path under /tv- answers 404), nginx never writes it to its access or error log, and sudo ersatztv-iptv-url --rotate replaces it.
  • The REST API needs two secrets. /api/ requires the administrator login and ErsatzTV's own API key, which ErsatzTV generates on your instance's first start.
  • Per-instance secrets. The administrator password, the IPTV token, ErsatzTV's database, API key and data-protection keys, and the TLS key are all created on your instance. The image ships without a database, so no two instances share a secret or a session.
  • Fails closed. ErsatzTV listens on ports 8409 and 8410 on every address (upstream has no option to bind loopback only), so a firewall table (inet ersatztv_guard, nftables) drops every packet to those ports that does not come from the instance itself. ersatztv.service will not start without it, and nginx will not start until first boot has created the login and the certificate.
  • HTTPS from first boot. nginx serves TLS 1.2 and 1.3 with a self-signed certificate created on your instance (TLS 1.1 is refused), marks cookies Secure and sends Strict-Transport-Security.
  • The host name comes from configuration, never from a request. First boot reads the instance's public DNS name (else its public or private IPv4 address) from EC2 instance metadata (IMDSv2) and pins nginx to it. Requests for any other host name get their connection closed; requests to the instance's IP addresses are redirected to that name. nginx hands ErsatzTV the configured name, https and the real peer address only, drops client forwarding headers and the sign-in header, refuses ErsatzTV's /internal paths, and refuses cross-site state changes and WebSocket upgrades.
  • The app cannot read instance metadata. ErsatzTV, the first-boot unit and nginx are blocked from the instance metadata service (169.254.169.254, fd00:ec2::254) by systemd IPAddressDeny; only the small unit that looks up the host name at first boot may use it.
  • Sandboxed. ErsatzTV runs as the unprivileged ersatztv user with a read-only system, no access to home directories, no new privileges, no devices and no capabilities; it can write only to /var/lib/ersatztv.
  • Dependencies. At build time the .NET runtime, all 132 NuGet packages (including transitive ones) and the bundled JavaScript libraries were checked against the OSV vulnerability database with no advisory found. ErsatzTV 26.11.6 includes the upstream fix for GHSA-qrh2-626h-76m2. Notes are in /opt/ersatztv/SECURITY-NOTES.txt.
  • No outbound calls of its own. ErsatzTV sends no telemetry and has no auto-update. Its home page shows release notes fetched from GitHub, and it contacts the Plex, Jellyfin or Emby servers you connect.
  • Hardened SSH and system. SSH allows keys only, no root login and no X11 forwarding. The AMI requires IMDSv2. SSH host keys and the machine ID are generated fresh on every instance. Only Ubuntu's own package archives are configured.

Prerequisites

  1. An AWS account and permission to launch EC2 instances
  2. An EC2 key pair in the Region you launch in
  3. Your media (video files, or a Plex, Jellyfin or Emby server ErsatzTV can reach)

Recommended instance type: m5.large. ErsatzTV itself is light; transcoding is the load. FFmpeg starts on demand for each channel being watched, and a 1080p H.264 stream transcoded on the CPU uses most of a vCPU, so choose a larger instance (for example m5.xlarge or a compute-optimised c5 type) for several simultaneous channels. Keep media on a separate EBS volume (Step 8) rather than the 16 GiB root volume.

Connecting to your instance

Version Operating system SSH user
ErsatzTV 26.11.6 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu

Step 1: Launch the instance

Option A: AWS Marketplace console

  1. Open the cloudimg Hardened SRS Streaming Media Server & Media Servers AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  2. Once the subscription is active, choose Launch your software.
  3. Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose ErsatzTV 26.11.6 on Ubuntu 24.04, then choose your Region.
  4. Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  5. Security group: the listing's default group opens SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and restrict 80 and 443 to the networks your viewers and media servers use. Do not open 8409 or 8410.
  6. Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDRs with your own:

SG=$(aws ec2 create-security-group --group-name ersatztv \
  --description "ErsatzTV live channels" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-viewer-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr <your-viewer-cidr>
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=500,VolumeType=gp3}' \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ersatztv}]'

The block device mapping adds an empty 500 GiB volume for your media (Step 8); leave it out if your media lives on a Plex, Jellyfin or Emby server. You do not need --metadata-options: the AMI already makes IMDSv2 required. Associate an Elastic IP if you want the address to survive a stop and start.

Step 2: Connect and find your login

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot takes about a minute after the instance starts. It sets the name ErsatzTV is served as (the instance's public DNS name when it has one), creates the TLS certificate, lets ErsatzTV create its database and keys, creates the administrator login and the IPTV token, and writes /var/lib/cloudimg/ersatztv-credentials.txt (mode 0600, root). These are the entries in that file (the secret values are not shown here):

sudo grep -E '^(ERSATZTV_URL|ADMIN_USERNAME)=' /var/lib/cloudimg/ersatztv-credentials.txt
sudo grep -oE '^[A-Z_]+=' /var/lib/cloudimg/ersatztv-credentials.txt
sudo stat -c '%a %U:%G %n' /var/lib/cloudimg/ersatztv-credentials.txt
ERSATZTV_URL=https://ec2-44-203-152-239.compute-1.amazonaws.com/
ADMIN_USERNAME=admin
ERSATZTV_URL=
ADMIN_USERNAME=
ADMIN_PASSWORD=
IPTV_PLAYLIST_URL=
IPTV_GUIDE_URL=
600 root:root /var/lib/cloudimg/ersatztv-credentials.txt
Value What it is for
ERSATZTV_URL The address of this instance's ErsatzTV web interface
ADMIN_USERNAME The administrator's user name (admin)
ADMIN_PASSWORD Administrator password, generated on this instance
IPTV_PLAYLIST_URL The M3U playlist under the secret IPTV address, for players that cannot sign in
IPTV_GUIDE_URL The XMLTV programme guide under the same address

To read the password and the IPTV addresses, run sudo cat /var/lib/cloudimg/ersatztv-credentials.txt in your own session and store them in your password manager.

Step 3: Confirm ErsatzTV is running

The first-boot units have finished, the firewall table, ErsatzTV and nginx are active, and the listening sockets are sshd and nginx on 22, 80 and 443 plus ErsatzTV's own 8409 (web interface and API) and 8410 (streaming). ErsatzTV binds every address, which is why the firewall table below exists:

systemctl is-active ersatztv-firewall.service ersatztv-host.service ersatztv-firstboot.service ersatztv.service nginx.service
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
cat /opt/ersatztv/VERSION
active
active
active
active
active
*:8409 users:(("ErsatzTV"))
*:8410 users:(("ErsatzTV"))
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.53%lo:53 users:(("systemd-resolve"))
127.0.0.54:53 users:(("systemd-resolve"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
ersatztv_version=26.11.6
ersatztv_source=https://github.com/ErsatzTV/legacy/tree/v26.11.6 (release asset https://github.com/ErsatzTV/ErsatzTV/releases/download/v26.11.6/ErsatzTV-Legacy-v26.11.6-linux-x64.tar.gz)
ersatztv_sha256=7920583c23c1b7cf492f4337a63447efe6f92b7936373f1a25bf1187541d3120
dotnet_runtime=10.0.14 (self-contained, inside the release)
ersatztv_channel=ersatztv-channel 0.2.4 cloudimg rebuild sha256 99bd3cdb9b936277e192fb41caaee5765c1e38d10241d44c4f3a7a75f6eb93ee (upstream binary sha256 48f201e597f408e00c5ad225b67eb1109cdc4297c7604747d903218fde69517e)
ffmpeg=n8.1.3-34-ga35157abb9-20261106 (/opt/ffmpeg, BtbN autobuild-2026-11-06-12-50)

The firewall table accepts loopback traffic and drops everything else sent to 8409 and 8410, so only nginx reaches ErsatzTV:

sudo nft list table inet ersatztv_guard
table inet ersatztv_guard {
    chain input {
        type filter hook input priority filter; policy accept;
        iif "lo" accept
        tcp dport { 8409, 8410 } drop
    }
}

Step 4: Prove the login and the web defaults

The web interface, the playlist and the REST API refuse a signed-out request, the health check answers, plain HTTP redirects to HTTPS, an unknown Host header gets no answer and TLS 1.1 is not offered. The last command is the image's own round-trip check: it signs in with this instance's administrator password, proves a wrong password and an anonymous request are refused, calls the REST API with and without its key, and fetches the playlist under the secret IPTV address (and proves a wrong token is refused):

H=$(sed -n 's/^set \$etv_host "\(.*\)";$/\1/p' /etc/nginx/ersatztv-hostvar.conf)
C=(--connect-to ::127.0.0.1:)
curl -sk "${C[@]}" -o /dev/null -w 'web UI, signed out -> %{http_code}\n' "https://$H/"
curl -sk "${C[@]}" -o /dev/null -w 'health -> %{http_code}\n' "https://$H/healthz"
curl -sk "${C[@]}" -o /dev/null -w 'playlist, signed out -> %{http_code}\n' "https://$H/iptv/channels.m3u"
curl -sk "${C[@]}" -o /dev/null -w 'REST API, signed out -> %{http_code}\n' "https://$H/api/version"
curl -s "${C[@]}" -o /dev/null -w 'http -> %{http_code}\n' "http://$H/"
curl -sk -o /dev/null -w 'unknown Host -> %{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
curl -sk "${C[@]}" -o /dev/null -w 'TLS 1.1 -> %{http_code}\n' --tlsv1.1 --tls-max 1.1 "https://$H/" || true
sudo ersatztv-selftest
web UI, signed out -> 401
health -> 200
playlist, signed out -> 401
REST API, signed out -> 401
http -> 301
unknown Host -> 000
TLS 1.1 -> 000
round-trip OK: admin signed in over https://ec2-44-203-152-239.compute-1.amazonaws.com/ (anonymous 401, wrong password 401), REST API refused without its key (401) and answered the version with it, IPTV token playlist served 1 channel(s) with tokenised stream URLs (wrong token 404)

--connect-to ::127.0.0.1: sends each request to this instance while keeping the real host name in the request, exactly as a browser would send it. 000 means the server closed the connection without an answer.

Step 5: Sign in to ErsatzTV

Browse to ERSATZTV_URL (https://<public-dns-name>/). The certificate is self-signed, so your browser warns once; accept it for this host, or install your own certificate (Step 10). The browser then asks for a user name and password: enter ADMIN_USERNAME and ADMIN_PASSWORD.

ErsatzTV opens on its home page, which runs a set of health checks and shows the release notes. On this image two checks show a warning, and both are expected:

  • FFmpeg Version says the FFmpeg build is not a current "ErsatzTV-ffmpeg" build. The image ships the upstream FFmpeg 8.1.3, which contains security fixes that ErsatzTV's own 8.1.2 package does not have. ErsatzTV works with it; the warning is informational.
  • Hardware Acceleration says channel 1 does not use NVENC, VAAPI or QSV. General-purpose EC2 instances have no GPU, so transcoding runs on the CPU (see Maintenance for GPU instances).

The ErsatzTV home page with the Health Checks table: FFmpeg Version and Hardware Acceleration show warnings, every other check is green, and the footer shows Legacy v26.11.6 and Next 0.2.4

Step 6: Channels and FFmpeg

Channels lists your channels. ErsatzTV creates channel 1 (named ErsatzTV) on first start, in MPEG-TS mode with the default 1920x1080 x264 aac FFmpeg profile. Use Add Channel for your own channels, the pencil to edit one (number, name, logo, streaming mode, FFmpeg profile), and the play button to preview it in the browser once it has a schedule.

The ErsatzTV Channels page showing channel 1, ErsatzTV, in MPEG-TS mode with the 1920x1080 x264 aac FFmpeg profile

Settings, then FFmpeg, is already pointed at the image's FFmpeg and FFprobe (/usr/local/bin/ffmpeg and /usr/local/bin/ffprobe). Leave those paths as they are. FFmpeg Profiles sets resolution, video and audio codecs and bitrates per channel; lower resolutions cost less CPU.

The ErsatzTV FFmpeg settings page with the FFmpeg path /usr/local/bin/ffmpeg, the FFprobe path /usr/local/bin/ffprobe and the default profile 1920x1080 x264 aac

The FFmpeg on the image, and its build options:

/usr/local/bin/ffmpeg -hide_banner -version | head -1
/usr/local/bin/ffmpeg -hide_banner -buildconf | grep -E -- '--enable-(gpl|version3|nonfree|libfdk)' | sed 's/^ *//'
ffmpeg version n8.1.3-34-ga35157abb9-20261106 Copyright (c) 2000-2026 the FFmpeg developers
--enable-gpl
--enable-version3

It is a GPL build with no non-free components (no --enable-nonfree, no libfdk-aac).

Step 7: Use the REST API

ErsatzTV's REST API (choose API at the top of the page for its documentation) needs the administrator login and the API key ErsatzTV generated on this instance. This example reads both into shell variables and hands them to curl through file descriptors, so neither appears on a command line, in your shell history or in the output:

H=$(sed -n 's/^set \$etv_host "\(.*\)";$/\1/p' /etc/nginx/ersatztv-hostvar.conf)
P=$(sudo grep '^ADMIN_PASSWORD=' /var/lib/cloudimg/ersatztv-credentials.txt | cut -d= -f2-)
K=$(sudo python3 -I -c 'import json; print(json.load(open("/var/lib/ersatztv/config/api-secrets.json"))["ApiKey"])')
curl -sk --connect-to ::127.0.0.1: --netrc-file <(printf 'machine %s login admin password %s\n' "$H" "$P") \
  -H @<(printf 'X-Etv-Api-Key: %s\n' "$K") "https://$H/api/version"; echo
curl -sk --connect-to ::127.0.0.1: --netrc-file <(printf 'machine %s login admin password %s\n' "$H" "$P") \
  -H @<(printf 'X-Etv-Api-Key: %s\n' "$K") "https://$H/api/channels" | python3 -I -m json.tool | head -20
unset P K
{"apiVersion":3,"appVersion":"v26.11.6-linux-x64"}
[
    {
        "id": 1,
        "number": "1",
        "name": "ErsatzTV",
        "ffmpegProfile": "1920x1080 x264 aac",
        "streamingMode": "MPEG-TS"
    }
]

From your workstation, drop --connect-to ::127.0.0.1: and use your instance's name; keep -k until you install a trusted certificate (Step 10).

Step 8: Add your media

ErsatzTV can read media from local folders on the instance, or from Plex, Jellyfin and Emby servers (Media Sources). For local media, attach an EBS volume (the CLI example in Step 1 adds one as /dev/sdf), put a file system on it and mount it, for example at /srv/media. ErsatzTV runs as the ersatztv user and needs to read the files; it cannot see anything under /home, so keep media out of home directories:

lsblk
sudo mkfs.ext4 -L media /dev/nvme1n1
sudo mkdir -p /srv/media
echo 'LABEL=media /srv/media ext4 defaults,nofail 0 2' | sudo tee -a /etc/fstab
sudo mount /srv/media
sudo mkdir -p /srv/media/movies /srv/media/shows
sudo chown -R ubuntu:ersatztv /srv/media
sudo chmod -R g+rX /srv/media

Check the device name with lsblk first (Nitro instances name EBS volumes /dev/nvme1n1 and so on), then copy your files up from your workstation with scp or rsync into /srv/media/movies and /srv/media/shows.

In ErsatzTV, open Media Sources, then Local. Each built-in library (Movies, Shows, Music Videos, Other Videos, Songs, Images, Remote Streams) holds one kind of media: choose the pencil next to one, add your folder (for example /srv/media/movies to Movies), and save. ErsatzTV scans it and fetches artwork and metadata. Then build a channel: create a Collection (or use a whole show), add it to a Schedule under Scheduling, and assign the schedule to a channel to generate its playout.

The ErsatzTV Local Libraries page listing the built-in libraries Movies, Shows, Music Videos, Other Videos, Songs, Images and Remote Streams, each with edit and delete buttons

Step 9: Watch your channels

IPTV players that can send a user name and password use https://<your-name>/iptv/channels.m3u for the playlist and https://<your-name>/iptv/xmltv.xml for the guide, with the administrator login.

Players that cannot sign in use the secret IPTV address from IPTV_PLAYLIST_URL and IPTV_GUIDE_URL. Show it (the token is masked here; run the command without the sed to see it):

sudo ersatztv-iptv-url | sed -E 's#/tv-[0-9a-f]{32}/#/tv-<token>/#'
IPTV playlist: https://ec2-44-203-152-239.compute-1.amazonaws.com/tv-<token>/iptv/channels.m3u
IPTV guide:    https://ec2-44-203-152-239.compute-1.amazonaws.com/tv-<token>/iptv/xmltv.xml

The stream addresses inside that playlist carry the same token. Treat the address like a password: anyone who has it can watch your channels (nothing else is reachable through it). To replace it, run sudo ersatztv-iptv-url --rotate; the old addresses stop working at once, so update your players.

Jellyfin and Emby: add an M3U tuner with the playlist address and an XMLTV guide provider with the guide address. HDHomeRun clients (such as Plex's Live TV & DVR) that let you enter a tuner by address can use https://<your-name>/tv-<token>: ErsatzTV's HDHomeRun discovery, lineup and stream addresses are all served under the token. If a client accepts only a bare host name, use the M3U playlist and XMLTV guide instead.

Media servers and TV apps usually refuse a self-signed certificate, so serve ErsatzTV under your own DNS name with a trusted certificate (Step 10) before connecting them.

Step 10: Serve ErsatzTV under your own DNS name

Point a DNS record (for example tv.example.com) at the instance's public or Elastic IP address, then tell the image to serve that name. It creates a new self-signed certificate for the name, pins nginx and the cross-site guard to it, updates the addresses in the credentials file and keeps the name across reboots:

sudo ersatztv-set-hostname tv.example.com

For a trusted certificate, obtain one for that name (for example with certbot's certonly mode using its own temporary web server, or from your organisation's CA), copy the certificate chain and key over /etc/ssl/ersatztv/ersatztv.crt and /etc/ssl/ersatztv/ersatztv.key (key mode 0600, root), and run sudo systemctl reload nginx.

Step 11: Check the security controls

ErsatzTV, its first-boot unit and nginx carry IPAddressDeny for the metadata addresses, first boot recorded that it could not reach the metadata service from inside its own unit, ErsatzTV runs sandboxed as ersatztv and requires the firewall table, and SSH runs with the cloudimg pins:

systemctl show -p IPAddressDeny ersatztv.service ersatztv-firstboot.service nginx.service
systemctl show -p User -p ProtectSystem -p ProtectHome -p NoNewPrivileges -p Requires ersatztv.service
sudo grep -o 'IMDS-REFUSED-IN-FIRSTBOOT.*' /var/log/cloudimg-firstboot.log
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|x11forwarding) '
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
User=ersatztv
ProtectHome=yes
ProtectSystem=strict
NoNewPrivileges=yes
Requires=ersatztv-firewall.service system.slice -.mount sysinit.target
IMDS-REFUSED-IN-FIRSTBOOT (http 000; IPAddressDeny in effect)
permitrootlogin no
passwordauthentication no
x11forwarding no

Maintenance

  • Change the administrator password: run sudo htpasswd -B -C 12 /etc/nginx/ersatztv.htpasswd admin (it prompts for the new password), then remove the old password from the credentials file (sudo nano /var/lib/cloudimg/ersatztv-credentials.txt) once you have stored the new one. nginx reads the change on the next request.
  • Operating system updates: unattended security upgrades are on. Reboot when /var/run/reboot-required exists; the firewall table, ErsatzTV and nginx start again by themselves.
  • ErsatzTV updates: ErsatzTV has no auto-update. To move to a newer release, launch the newer version of this image when the listing offers it and copy /var/lib/ersatztv across with ErsatzTV stopped.
  • Backups: ErsatzTV keeps its database, keys, artwork cache and settings in /var/lib/ersatztv (on the root volume). Take an EBS snapshot, or stop ErsatzTV (sudo systemctl stop ersatztv) and copy the directory. Snapshot your media volume separately.
  • GPU transcoding: on a GPU instance, install the NVIDIA driver and add a systemd drop-in for ersatztv.service with PrivateDevices=false so ErsatzTV can use the GPU, then pick an NVENC FFmpeg profile.
  • Ubuntu's nftables service: if you enable nftables.service, its /etc/nftables.conf starts with flush ruleset, which removes the ErsatzTV table. Run sudo systemctl restart ersatztv-firewall afterwards, or add the table to that file.
  • Logs: sudo journalctl -u ersatztv for ErsatzTV (also Support, then Logs, in the web interface), /var/log/nginx/ for web requests (the secret IPTV address is never written to the access or error log), /var/log/cloudimg-firstboot.log for first boot.

Troubleshooting

  • The web page does not load right after launch: first boot is still running. sudo journalctl -u ersatztv-host.service -u ersatztv-firstboot.service -f shows its progress.
  • Browser shows "connection closed": nginx answers only the configured name and redirects the instance's own IP addresses to it. Use ERSATZTV_URL, or run sudo ersatztv-set-hostname <name> for a new name.
  • The browser keeps asking for a password: use ADMIN_USERNAME and ADMIN_PASSWORD from the credentials file exactly; the user name is admin.
  • A player gets 401 or 404: 401 means it is using /iptv/... without a login; give it the secret IPTV address from sudo ersatztv-iptv-url instead. 404 under /tv- means the token is wrong or out of date (for example after a --rotate).
  • ErsatzTV does not start: systemctl status ersatztv-firewall must be active; ErsatzTV refuses to start without its firewall table.
  • New media does not appear: check the folder is readable by the ersatztv user (sudo -u ersatztv ls /srv/media/movies) and is outside /home, then rescan the library from Media Sources.
  • The end-to-end check: sudo ersatztv-selftest signs in, calls the API, fetches the playlist under the secret address and prints round-trip OK when everything works.

Licensing

ErsatzTV is licensed under the zlib licence (Zlib); the text is on the instance in /opt/ersatztv/LICENSE. The ErsatzTV "Next" streaming engine (ersatztv-channel) is MIT-licensed (/opt/ersatztv/LICENSE.ersatztv-channel). FFmpeg is licensed under the GNU General Public License (this build enables GPL and version 3 components and no non-free code); its licence is in /opt/ffmpeg/LICENSE.txt and /opt/ffmpeg/SOURCE.txt says where to obtain its complete corresponding source. /opt/ersatztv/SOURCE.txt points to the ErsatzTV source at github.com/ErsatzTV/legacy. cloudimg is not affiliated with, endorsed by or sponsored by the ErsatzTV project or its authors, or by Plex, Jellyfin, Emby or HDHomeRun.

Support

cloudimg provides 24/7 technical support for this image by email and live chat: deployment, first login, HTTPS and DNS setup, IPTV and media server connections, upgrades and troubleshooting. Contact support@cloudimg.co.uk.