Docker - Mini server as PVR receiver for DVB-T/S/C
Television has changed drastically in recent years. With the rise of streaming services, traditional TV reception is increasingly taking a back seat. Nevertheless, I did not want to give up tried-and-tested television, but I had to rethink my central TV setup when my Dreambox stopped working.
Central TV tuner / (PVR) server via WLAN for all TVs.
For a long time, I used a PVR hard-disk receiver as an addition to my TV, specifically a Dreambox DM7020HD with 2 DVB-C tuners. Originally I used it to record certain programs automatically so I could skip the commercials; later I also used it to watch live TV purely over WLAN with a FireTV stick, without a network or coax cable at the TV, and to stream the recorded programs: centrally over LAN / WLAN for all TVs in the house. Unfortunately, the Dreambox gave up the ghost after more than 10 years. While looking for a replacement, I realized that new devices with a similar range of functions to the Dreambox are in short supply. It seems as if the market for hard-disk receivers has shrunk massively over the last 10 years thanks to various streaming services. Not willing to pay several hundred euros for 7-year-old hardware or take out a subscription for an IP streaming service, I came up with the idea of simply equipping my existing mini server, a Beelink 5560U, with suitable DVB tuners: I installed Ubuntu and Docker as the operating system, which also allows the server to run other services such as my home automation. The Beelink 5560U uses very little power and is powerful enough to take on the PVR service as well. This allowed me to remove the Dreambox without replacing it: one less device consuming standby power.
Existing Docker mini server replaces the PVR hard-disk receiver
Not only the receivers, but also the available software has usually been around for a while. Tvheadend offers an enormous range of functions, while the web interface does not look fresh in every corner. In everyday use that bothers me very little: once everything is configured, the TVs access the server through clients such as Kodi anyway. What matters most to me are central recordings, timeshift, network streaming and direct transcoding. Tvheadend can also work with conditional-access backends such as OSCAM. I deliberately show the technical connection further down in generic form: without any provider-specific details and without card-specific keys or activation parameters.
Software: TVHeadend - Versions: "latest" not always sufficiently tested?
TVHeadend is still actively developed, but now only as a so-called rolling release. Every change flows directly into "latest" on GitHub, which may mean that certain changes are tested less thoroughly? At least I repeatedly had the problem that after a container update essential functions such as transcoding recordings only worked partially or no longer worked at all, the audio was no longer synchronized, or the picture simply stayed dark. Some of the update problems were caused by a change in the configuration and could be fixed by rebuilding the settings. For this reason I do not use "latest" for the Docker containers in this article, but a version that I have been using for quite some time without problems. If I change it, I will add newer versions to this article after I have tested them successfully.
Hardware USB problematic? Tuner: DVB-C (USB)
For DVB-C TV reception I used the following USB stick:
If you already have a Docker setup running, you do not need to worry about dependencies on other software packages. The PVR service and its container can simply be started in the terminal with "docker run": Docker Basics
A container is an isolated environment independent of the operating system (OS):
When a container is first launched, Docker independently loads all the necessary sources
from the internet.
Docker can be installed on Windows, macOS or an Linux Distribution
docker run -d \
--name=tvheadend \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Europe/Berlin \
-e RUN_OPTS= `#optional` \
-p 9981:9981 \
-p 9982:9982 \
-v /path/to/tvheadend/data:/config \
-v /path/to/recordings:/recordings \
--device /dev/dri:/dev/dri `#optional GPU-Passthrough` \
--device /dev/dvb:/dev/dvb `#optional USB DVB-Adapter` \
--restart unless-stopped \
linuxserver/tvheadend:version-26a14aa3
The DVB-C USB stick (tuner) is mapped into the container with --device.
After startup, Tvheadend can be opened in the browser using the IP address of the Docker server and port 9981:
During setup I had the problem that no channels were found. Only after I added the "linux-firmware-haupauge" package to the host operating system did the channel scan work, hence the commands used here for the host operating system, in my case Ubuntu:
sudo add-apt-repository ppa:b-rad/kernel+mediatree+hauppauge
sudo apt-get install linux-firmware-hauppauge
Because of my positive experience with the Haupauge DVB-C adapter, I wanted to use the Hauppauge WinTV Nova S2 USB stick for a DVB-S setup. The hardware version I received was Rev. BBH9, which according to the manufacturer's website - as of October 2025 - is not supported: only Rev B6H9 or older versions are supported.
Not supported: WinTV-NOVA-S2: BBH9:

I was nevertheless able to activate the USB stick with the following commands:
cd /lib/firmware/
sudo wget https://hauppauge.s3.us-east-1.amazonaws.com/linux/dvb-demod-m88ds3103c.fw
sudo apt install linux-mediatree

I was then able to use the stick for certain channels, but unfortunately not for all of them: certain muxes were not found and even when using an existing mux from another TV adapter, the WinTV-NOVA-S2 simply could not tune certain channels in my setup.
For comparison: with Network Astra192e, 1336 services were found after a "Force Scan"; with the Haupauge tuner (Astra192e-Haupauge), only 670 services work.
USB DVB adapters require a driver in the host operating system, which can make setup difficult or sometimes impossible. Support for the hardware and drivers in future operating-system updates is also not guaranteed.
Tuner: DVB-S USB vs. SAT>IP
Having started with DVB-C, I have now also tested my setup with DVB-S. The hardware can consist of a satellite dish and a SAT>IP server for TVHeadend. SAT>IP is completely unproblematic with regard to the required drivers because the drivers are loaded internally by the SAT>IP server: TVHeadend only needs a network connection to the SAT>IP server. [[!updateOrCreate.Amazon.Link:B01LT6IB3Q.Satanlage-de!]]
[[!updateOrCreate.Amazon.Link:B0FK5PT148.Digibit-twin-de!]]
The Digibit Twin SAT>IP server allows very stable operation and simpler installation because the server can be placed near the satellite dish and connected from there to the TVHeadend server with a network cable. The only small disadvantage: my Digibit Twin had a measured standby consumption of 3 watts even when no tuner was active, although the technical data states < 0.3 watts. With one active tuner, consumption is around 6 watts and with 2 tuners around 7.5 watts.
Commissioning SAT>IP Digibit Twin
After connecting it to the network, the Digibit Twin obtains an IP address via DHCP. Entering that address in the browser brings up a password dialog from the SAT>IP server:
The default password of the TELESTAR Digibit Twin is "admin".
After logging in, there are exactly 3 menu items: Status, Network and System:
The settings are therefore limited to the IP address, changing the access password and the option of installing new firmware versions.
The Digibit Twin only appeared in TVHeadend (Configuration / DVB Inputs / TV Adapters) after I specified the path to the SAT>IP server's description.xml under RUN_OPTS in Docker:
docker run -d \
--name=tvheadend \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Europe/Berlin \
-e RUN_OPTS= --satip_xml http://192.168.1.144:38400/description.xml
--network host \
-v /path/to/tvheadend/data:/config \
-v /path/to/recordings:/recordings \
--restart unless-stopped \
linuxserver/tvheadend:version-26a14aa3
To use several SAT>IP servers, multiple SAT-IP addresses can also be entered one after another here. I also had to change the network mode to "host" so that communication over the port range required for SAT>IP works without restriction.
Connecting a smartcard reader with OSCAM to Tvheadend
The following configuration shows the technical connection of a smartcard reader with OSCAM and Tvheadend. Use it only with cards and services you are authorized to use. Provider- or card-specific keys, boxkeys, RSA keys and activation parameters are deliberately not part of the example.
Hardware: USB smartcard reader
A reader such as the EasyMouse can appear on the Linux host as /dev/ttyUSB0, for example. The device path actually in use can be checked with commands such as ls -l /dev/ttyUSB* or dmesg. Voltage, operating mode and clock rate on the card reader must match the smartcard actually being used; these values should not simply be copied from a different card.
Hardware example: [[!updateOrCreate.Amazon.Link:B0CXY88F1J.easymouse-de!]]
Docker Compose: Tvheadend and OSCAM
The reader is passed through only to the OSCAM container. Tvheadend then communicates with OSCAM over the CAPMT/DVBAPI port. The example deliberately remains independent of any particular TV provider:
services:
tvheadend:
image: linuxserver/tvheadend:version-26a14aa3
container_name: tvheadend
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- RUN_OPTS= --satip_xml http://192.168.1.144:38400/description.xml # optional for SAT>IP
volumes:
- ./data:/config
- /mnt/store/daten/recordings:/recordings
- ./TVHadmin-JS:/usr/share/tvheadend/src/webui/static/TVHadmin-JS
network_mode: host
devices:
- /dev/dri:/dev/dri # optional GPU
- /dev/dvb:/dev/dvb # optional USB-DVB
restart: always
oscam:
image: lscr.io/linuxserver/oscam:latest
container_name: oscam
group_add:
- dialout
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
volumes:
- ./oscam:/config
ports:
- 8888:8888 # WebUI - expose only on a trusted network
- 6666:6666 # DVBAPI/CAPMT
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
restart: unless-stopped
If the reader gets a different name on the host, the device path must be adjusted accordingly. With several USB serial devices, a stable path under /dev/serial/by-id/ is usually more robust than assuming a fixed /dev/ttyUSB0.
OSCAM: basic configuration
The following files contain only the generic connection between the reader, OSCAM and Tvheadend. The values for a specific card must come from the documentation intended for your own card.
oscam.conf:
[global]
logfile = stdout
[dvbapi]
enabled = 1
au = 1
pmt_mode = 1
request_mode = 1
listen_port = 6666
user = tvh
boxtype = pc
[webif]
httpport = 8888
httpuser = admin
httppwd = [SECURE PASSWORD]
httpallowed = 127.0.0.1,10.0.0.0-10.255.255.255,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255
oscam.user:
[account]
user = tvh
au = 1
group = 1
oscam.server:
[reader]
label = smartcard
protocol = mouse
device = /dev/ttyUSB0
caid = [CAID OF YOUR OWN AUTHORIZED CARD]
detect = cd
group = 1
emmcache = 1,3,2,0
Depending on the card, the reader may require additional parameters. I do not publish provider- or card-specific cryptographic values here; they are not necessary to understand the Docker, reader and Tvheadend connection either.
Adding OSCAM in Tvheadend
In Tvheadend, a sufficiently high view level must first be enabled so that the CA settings are visible:
A CAPMT client for OSCAM can then be created under the CA settings. In the Compose setup shown above, Tvheadend runs in the host network and OSCAM publishes port 6666 on the host; accordingly, the connection can point to the host and port 6666. OSCAM's web interface on port 8888 should not be exposed unprotected to the Internet.
ORF2: PID changes between national and regional programming
ORF 2 has a specific Tvheadend bug when switching between national and regional programming. The open GitHub issue #1973 describes a frozen and subsequently black picture even though the service itself continues to exist. The technically interesting part is the stream reconfiguration: video, audio and teletext components, or their PIDs, can change while the service is running.
For troubleshooting, the PMT, PID and stream changes are what matter. The ORF2 documentation in this article therefore does not require any smartcard, OSCAM, key or other provider-specific decryption parameters.
As of October 2, 2026: the combined pull request #2206 is open, not in draft status and not merged. It bundles the changes relevant to this ORF2 bug from #2204 and #2205. Issue #1973 is also still open. This means the patch is at the testing and review stage, but is not yet part of master.
Earlier watchdog, priority and restart approaches could only catch the problem from the outside. The actual root-cause analysis, reproducible tests and the resulting patch are documented in the sub-story One year of ORF2 trouble: building my own Tvheadend patch with AI help.
For the status, it is worth keeping a clean distinction between a locally tested patch, an open pull request, a merge into master and a version that is actually delivered later. For the bug described here, #2206 is currently the relevant open pull request.
Here are a few screenshots of my settings in TVheadend for DVB-C
First I created a network under Configuration / DVB Inputs / Network:
The network can then be assigned to the connected TV adapters, in my case the two adapters of the Hauppauge WinTV-dualHD TV stick, and scanned for channels:
The TV channels should appear under "Services" after a while.
Channel search DVB-S: TV adapter, network
A prerequisite for operation is, of course, that the TV adapters show up in TVHeadend:
A network is required for the channel scan, here for Astra 19.2E:
The network must then be assigned to at least one TV adapter:
Force Scan starts the search for muxes and adds the channels found as services:
Direct transcoding to H.265 (HEVC)
If you are using a reasonably powerful mini PC, you can transcode recordings on the fly as H.264 or, even better, H.265, which means they require very little storage while still maintaining very good quality. For HD quality, H.265 reduces the stream from around 10 kB/s to 2.5 kB/s. The CPU in my mini PC reaches around 25% utilization per stream; even with up to 3 tuners in use, that is no problem.
I used "MPEG-TS/av-lib" as the container because otherwise jumping to a different point in time (skip) only works with a considerable delay. I disabled "Hardware acceleration" because I found that the recordings have better quality that way. I have not yet got to the bottom of the cause.
Because the mini PC has 32 GB of RAM, I changed timeshift to "RAM only" to spare the SSD.
Recording profile: my adjustments
The stream profile created earlier can be used in the recording profile:
The "Use EPG running status" setting causes recordings to be cut off on certain channels. The setting can be disabled per channel, in which case the recording profile's "Pre-recording padding" and "Post-recording padding" are used:
For certain "auto recordings", I then adjusted the duplicate handling and DVR file retention time:
Wrong audio track: e.g. audio description
After switching from DVB-C to DVB-S, a different audio track became the default on certain TV channels: some programs were then in the original English by default or came with audio description. This behavior can easily be adjusted with a "Stream Filter":
IGNORE can be used to ignore certain audio streams. With Action: "EMPTY", the audio stream can still be used if no other stream is available. The following example:
Language German (ger) should be used: USE; "mis" should not be used: IGNORE.
The last filter, EMPTY, is a fallback in case no audio stream remains because of the first two filters, for example a stream that is not "ger". ("mis" would then also be possible in the fallback, so the filter for "mis" would not necessarily have to be ...)
Delete the oldest recordings first
TVHeadend stops recordings when the limits in the recording profile have been exceeded: "Manage free disk space in MiB" and "Manage used disk space in MiB".
To delete the oldest recordings and make room for new ones, I run a small Bash script on the host operating system:
#!/bin/bash
RECORDINGS_DIR="/mnt/store/daten/recordings"
STORE_MOUNT="/mnt/store"
MIN_FREE_GB=60
MAX_USED_GB=300
get_free_gb() {
df -BG --output=avail "$STORE_MOUNT" | tail -1 | tr -dc '0-9'
}
get_used_gb() {
du -sBG "$RECORDINGS_DIR" | awk '{print $1}' | tr -dc '0-9'
}
while true; do
free_gb=$(get_free_gb)
used_gb=$(get_used_gb)
if [[ $free_gb -ge $MIN_FREE_GB && $used_gb -le $MAX_USED_GB ]]; then
break
fi
# Find the oldest file (null-terminated)
oldest_file=$(find "$RECORDINGS_DIR" -type f -printf '%T@ %p\0' | sort -z -n | head -zn 1 | cut -z -d' ' -f2-)
if [[ -z "$oldest_file" ]]; then
echo "No more files to delete."
break
fi
# Remove the file safely
printf 'Deleting %s to free up space...\n' "$oldest_file"
rm -f -- "$oldest_file"
done
The script monitors the recordings folder, in my case /mnt/store/daten/recordings, as well as the free space on /mnt/store, and deletes old recordings if less than 60 GB is free or the recordings use more than 200 GB.
The recordings are cleaned up daily via a Cron entry (sudo crontab -e):
3 3 * * * sudo /scripts/cleanup-recordings.sh > /dev/null 2>&1
Access on the go via the Internet: secure and without VPN
To be able to schedule certain programs while away or stream live TV to my smartphone, I gave the default user in Tvheadend a username and password. An additional DNS entry on the Internet and the existing port forwarding including reverse proxy allow me to access it over the Internet without a VPN, see: secure HTTPS connection: Traefik Reverse Proxy + Let's Encrypt
Here is the Docker Compose configuration I use:
/conf/tvheadend.yml file - remote access
http:
routers:
tvh:
entrypoints: "websecure"
service: "tvh@file"
rule: "Host(`tv.domain.tld`)"
tls:
certresolver: "myresolver"
services:
tvh:
loadbalancer:
servers:
- url: "http://192.168.1.5:9981"
Include via:
version: "3.3"
services:
traefik:
image: "traefik"
container_name: "traefik"
command:
...
- "--providers.file.directory=/etc/traefik/conf"
...
volumes:
...
- "./conf:/etc/traefik/conf"
Client access: TVs and mobile devices
For access on the local network, I use a FireTV stick on each TV. Kodi is also available as an Android app, which turns a phone or tablet into a TV. The Android app occasionally crashes for me when starting. If the app no longer starts, clearing the cache in the app settings helps. VLC can also be used as an alternative: in VLC stream: http://tvheadendip/playlist/channels.m3u?profile=pass (Basic Authentication must be allowed in TVHeadend).
I currently access Tvheadend over the Internet through its web interface. Because the web interface is not optimized for mobile devices, I created a fork of TVHadmin-JS on GitHub and adapted it to my needs: https://github.com/LiBe-net/TVH-Mobile.
New WebUI
A more modern WebUI is also being developed.
(https://github.com/tvheadend/tvheadend/commit/f23e5f80db79a1ba7614eac8205f8c93a6479cd5, as of July 2026)
Alternative receiver hardware for SAT?
Instead of the Tvheadend server setup described here, a conventional receiver can also be used. As one example, the Octagon SF8008 offers a dual SAT tuner and, depending on the firmware, can provide additional network functions. A built-in card reader only says something about the hardware; which cards and services it is intended for depends on the respective reception system.
[[!updateOrCreate.Amazon.Link:B0CVR2WT1X.OCTAGONSF8008-de!]]
Conclusion
Switching from a classic PVR hard-disk receiver to a mini-server setup with Docker and DVB tuners has proven to be a practical solution. A mini PC such as the Beelink 5560U and Tvheadend under Docker provide a solid foundation for TV reception and streaming to multiple TVs: without coax cables, purely over Ethernet or WLAN. The setup also allows recordings to be transcoded directly to H.265 and accessed over the Internet.
ORF 2 Freezes Tvheadend: My Codex Patch Is Now in master
During a test with ORF 2, everything looked unspectacular at first: the stream was running, then the regional news started – and at some point the picture was gone. Not briefly gone, not with a small glitch, but gone in a way that meant Tvheadend could practically no longer recover the channel after the switch. The return from the regional to the national program was particularly easy to reproduce: frozen picture, stop and play had no effect, switching channels did not help either. In... ... continue reading
- Central TV tuner / (PVR) server via WLAN for all TVs.
- Existing Docker mini server replaces the PVR hard-disk receiver
- Software: TVHeadend - Versions: "latest" not always sufficiently tested?
- Hardware USB problematic? Tuner: DVB-C (USB)
- Tuner: DVB-S USB vs. SAT>IP
- Connecting a smartcard reader with OSCAM to Tvheadend
- ORF2: PID changes between national and regional programming
- Here are a few screenshots of my settings in TVheadend for DVB-C
- Channel search DVB-S: TV adapter, network
- Direct transcoding to H.265 (HEVC)
- Recording profile: my adjustments
- Wrong audio track: e.g. audio description
- Delete the oldest recordings first
- Access on the go via the Internet: secure and without VPN
- Client access: TVs and mobile devices
- New WebUI
- Alternative receiver hardware for SAT?
- Conclusion
({{pro_count}})
{{percentage}} % positive
({{con_count}})
























Questions / Comments