Overview
Jellyfin is my media server for films, series and music. I also wanted live free-to-air TV in the same interface, so I didn’t need a separate app or a set-top box in every room. Jellyfin doesn’t handle tuners or channel guides very well by itself. The usual approach is to put a PVR backend behind it, and I chose NextPVR.

The design goals were:
- One interface for everything. Live TV shows up in Jellyfin next to the library.
- Few moving parts. No extra VMs and no Docker layer.
- Channel and guide management stays in a tool built for it.
This post covers how the pieces fit together, an outage that broke every channel at once, and getting a Jellyfin app onto the bedroom TV. Hostnames and addresses are left out, and the snippets use placeholder values.
Architecture
Both services run natively under systemd in the same LXC container on my Proxmox cluster. There is no Docker on this host. Jellyfin and NextPVR are well-behaved Linux services, so a container runtime inside an LXC container would only add another layer to debug.
- NextPVR holds the channel list, the electronic programme guide (EPG) and any recordings. It pulls live streams from upstream sources and serves them over its own HTTP API.
- The Jellyfin NextPVR plugin logs in to NextPVR’s API with a pairing PIN and keeps a session ID, which it uses to list channels and guide data and to request streams.
- Jellyfin’s bundled ffmpeg takes the MPEG-TS stream from NextPVR and transcodes it for whatever client is playing.
The plugin talks to NextPVR over loopback, so stream traffic between them never crosses the network.
Why NextPVR
You can point Jellyfin straight at an M3U playlist and XMLTV guide, use a hardware tuner such as an HDHomeRun, or put a PVR backend in front. I picked NextPVR because:
- Channel management is better. Channel definitions are separate from the mapping that decides which channels are shown, so I can hide a channel without losing its guide or recording history.
- It can record. Recordings belong to NextPVR, and Jellyfin only displays them.
- It runs natively on Linux as a normal systemd service.
Channels, guide data and recording metadata live in one SQLite database, so backup is simple. For example:
| |
The Outage: “Streaming Failed (transcoder exited)”
The setup ran fine for a while. Then every live channel started failing with the same message:
Streaming Failed (transcoder exited)
Every channel failing at once pointed at the link between Jellyfin and NextPVR rather than at an upstream source.
Following the error down
The Jellyfin message only says ffmpeg exited. The why is in the per-attempt transcode logs. On a default Debian install, something like this finds them:
| |
They showed:
| |
ffmpeg exited with code 183. It wasn’t failing to transcode a real stream; it was being handed something that wasn’t video at all, most likely an error response from NextPVR.
The root cause
The cause was a single NextPVR setting, AllowRemoteAPI, set to false. From the name you’d expect it to affect other machines only. In practice it also rejected the plugin, even over loopback. The plugin’s login failed silently, its stream requests were refused, and ffmpeg received garbage.
The relevant excerpt of NextPVR’s config looks like this:
| |
If you enable it, it’s worth making sure only the hosts that need the API can reach it, for example with a host firewall in the container. I backed up the config, changed the setting, and restarted in order, backend first:
| |
Restarting Jellyfin second makes the plugin log in again against a backend that’s already up. Its stored session ID changed, confirming a fresh login, and live TV worked again.
Losing Some Channels
At about the same time, three channels from one national broadcaster stopped working. The cause was upstream: my source no longer carried them. The broadcaster’s own streams use per-session signed CDN URLs that rotate often, so there’s no stable URL to give NextPVR. Getting around that would mean bypassing their access controls, and I decided not to.
Instead I removed only those channels’ mappings, keeping their definitions and guide and recording history. If a legitimate source appears later, I can map them again without losing anything.
A Jellyfin App for the Bedroom TV
The bedroom TV is a 2020 Samsung running Tizen. Samsung’s store doesn’t carry Jellyfin, but the Jellyfin project publishes an official guide for building the Tizen app and installing it in Developer mode. It works well, after more detours than the guide suggests.
Building it
The build compiles Jellyfin’s web client, wraps it in the Tizen app project, and packages a signed .wgt with Tizen Studio’s command-line tools. Things that weren’t obvious:
- Match the web client to your server. I built from the release tag matching my server version, not the newest branch.
- Check the Node.js version. The web client needed a newer Node.js than my build machine had, so I installed a separate copy for the build.
- The signing tools assume a desktop. On a headless box, Tizen Studio tries to store certificate passwords in a desktop keyring that isn’t there, then the packaging step crashes looking for a terminal prompt. There are workarounds, but expect trial and error.
A plain Tizen developer certificate was enough; I didn’t need a Samsung account certificate. Packaging and installing look roughly like this, with a made-up profile name and a documentation address:
| |
Getting it onto the TV
- Developer mode needs a real number pad. You enable it by typing 12345 on the Apps screen. The remote’s on-screen pad did nothing, and nor did key presses from Home Assistant. The number pad in Samsung’s SmartThings phone app worked first time.
- The TV only trusts its own subnet. Its install port accepted a connection from my build machine on another VLAN, then dropped it. This TV also ignores remote-control commands from other subnets, which is why Home Assistant needs a presence on the TV’s network. I gave the build machine a temporary address there, installed the app, and removed the address.
The certificate the TV didn’t trust
Once installed, the app couldn’t reach the server. My internal certificates come from Let’s Encrypt via my DNS-01 automation and used ECDSA keys, whose chain ends at a newer root a 2020 TV doesn’t have. I reissued that one server’s certificate with an RSA key, which chains to the older root the TV trusts, and removed the old ECDSA renewal entry so a renewal can’t switch it back. With certbot, for example:
| |
1080p screen, 4K library
The screen is 1080p, and my newer films are 4K HDR files the server’s GPU can’t convert on the fly. The TV tried direct play and sat on the poster. For films we watch in the bedroom, I now keep a 1080p copy in a separate library.
What Worked / What Didn’t
What worked
- Both services natively in one container. No network hop for streams, no container runtime, and systemd handles restarts.
- Backend separate from frontend. NextPVR handles TV, Jellyfin handles playback.
- Mappings separate from definitions. Dead channels hidden without destroying history.
- Dated backups before every edit. Any change could be undone with one command.
- Building the TV app myself. Fiddly once, but it now behaves like any store app.
What didn’t
- Error reporting across the plugin boundary. A failed login between two services on one host surfaced as a generic transcoder error.
- Unofficial upstream sources. Channels can disappear without warning.
- A misleadingly named option.
AllowRemoteAPIcontrols more than “remote” access. - Old devices and new crypto. An ECDSA-only certificate quietly locked out a TV that’s only a few years old.
Lessons Learned
- “Transcoder exited” is a symptom, not a diagnosis. Go to the per-attempt ffmpeg logs. “Invalid data found when processing input” usually means ffmpeg got an error page or nothing.
- When everything fails at once, look at the link between services. One bad channel points upstream; all of them points at authentication or config.
- Don’t assume “remote” means “not localhost”.
- Restart order matters. Backend first, frontend second, then confirm the new session.
- Hide data instead of deleting it.
- Check certificate chains against your oldest client. RSA is still the safe choice for anything a TV has to trust.
- Some problems shouldn’t be engineered around. Bypassing a broadcaster’s access controls wasn’t the right call for my lab.
Live TV is back in Jellyfin, on every screen in the house. The next time “transcoder exited” appears, I know which log to check first.

Comments