Media Server
A Proxmox virtualization stack built entirely from decommissioned hardware, running Jellyfin, live IPTV over multicast, ZFS storage and VLAN segmentation. Started because the gym WiFi was too slow; became the project that taught me infrastructure.
Overview#
A media server that turned into a virtualization stack, and then into the single most useful thing on my CV. A homelab in everything except the home - it lives at work, on work hardware. The real one is still waiting on a house to put it in.
It runs Jellyfin and live television for people who have no idea there's a virtualization host, a ZFS pool and a segmented network underneath. That's the point. It's a real service with real users, which is precisely why it was worth building.
The full story, including everything that went wrong, is in The media server that taught me infrastructure.
None of the hardware was purchased for this project. All of it came out of a server room inventory: decommissioned, replaced or forgotten equipment that was otherwise heading for disposal.
Why it exists#
The WiFi covering the gym was too weak to stream anything without buffering. Rather than fight the wireless coverage, I moved the content onto the local network where there was gigabit of unused capacity.
The second reason mattered more: I wanted to learn Linux and infrastructure on something with real consequences. A lab VM breaking costs nothing and teaches nothing. A media server going down gets noticed, and that pressure turned out to be the most effective teacher I've had.
Architecture#
flowchart TD
subgraph Host["Proxmox VE on a Dell OptiPlex Micro"]
J[LXC: Jellyfin<br/>Quick Sync transcoding]
T[LXC: TVHeadend]
end
Z[(Local ZFS RAIDZ1<br/>reclaimed disks)] --> J
S[(Synology DS923+<br/>NFS, long-term)] --> J
IP[IPTV multicast<br/>dedicated VLAN] --> T
T --> J
J --> CL[Clients: movies, shows,<br/>music, live TV]
Virtualization. Proxmox VE, with every service in its own unprivileged LXC container. Snapshot before a change, roll back in seconds, back up on a schedule.
Storage. Media on a local ZFS RAIDZ1 pool built from reclaimed drives, chosen for checksumming and snapshots after a bad experience with silent read errors. The Synology continues to serve long-term storage over NFS. The OS lives on its own SSD.
Transcoding. Intel Quick Sync on the host iGPU, passed through to the Jellyfin container. Turned a machine considered too old to keep into one that handles multiple simultaneous streams.
Networking. Separate VLANs for administration, storage, services and IPTV, with inter-VLAN routing and firewall rules between them.
Live TV. TVHeadend receives multicast television streams on the IPTV VLAN, maps services to channels, and presents them to Jellyfin so live television appears alongside the on-demand libraries.
The evolution#
Each rebuild was driven by a limitation of the previous one:
| Phase | Change | Why |
|---|---|---|
| 1 | Jellyfin on Synology | Simplest thing that worked |
| 2 | Reclaimed disks in a DAS | Storage was the first ceiling |
| 3 | Ubuntu on an OptiPlex | DSM hid too much to learn from |
| 4 | Docker | Dependency isolation and portability |
| 5 | Quick Sync | CPU transcoding couldn't keep up |
| 6 | EqualLogic SAN | Failed, unreliable, migrated away |
| 7 | ZFS RAIDZ1 | Checksums and snapshots after the SAN |
| 8 | Proxmox + LXC | One OS for everything was too fragile |
| 9 | VLANs | Flat network stopped being acceptable |
| 10 | TVHeadend + multicast | Live TV, and the best networking lesson available |
What it taught me#
Old enterprise hardware isn't automatically better. The EqualLogic SAN looked like an upgrade over consumer disks in a plastic enclosure. It had been decommissioned for a reason, and weeks of I/O errors proved it. Knowing when to abandon an approach is an engineering skill.
Permissions are the same lesson three times. Docker bind mounts, GPU device access, and unprivileged LXC UID mapping are one problem wearing different hats: a process runs as a numeric ID, and the filesystem only cares about the number.
Multicast fails silently. A stream that stopped until TVHeadend was restarted turned out to be IGMP group membership expiring with nothing to refresh it. The network was correctly forwarding traffic to nobody. Diagnosing it with bridge mdb, ip maddr and tcpdump taught me more than any lab.
Constraints drove the learning. With no budget, every capability had to come from understanding something well enough to repurpose it.
What it fed into#
The VLAN design and inter-VLAN routing here went directly into a production network migration at work. The Linux, Docker and storage work underpins the Tracker and the fiventhree.com domain, both of which run on infrastructure I manage the same way.
Roadmap
- Jellyfin on SynologyShipped
- the starting pointNext
- Reclaimed disks into a DAS arrayShipped
- Split rolesShipped
- Ubuntu app server plus NAS storageNext
- Containerised with DockerShipped
- Intel Quick Sync hardware transcodingShipped
- Migrated off the EqualLogic SAN after it proved unreliableShipped
- Rebuilt storage on ZFS RAIDZ1Shipped
- Rebuilt on Proxmox with per-service LXC containersShipped
- VLAN segmentation for admin/storage/services/IPTVShipped
- Live IPTV via TVHeadend multicast into JellyfinShipped
- Off-site backup for configuration and metadataIn progress
- Monitoring and alerting on pool healthNext
- A documented rebuild runbookNext
Development log
Every article written against this project, newest first.
Widening the same tunnel: a directory, a hypervisor console, and a redirect loop that wasn't mine to fix
The WireGuard bridge that reached Jellyfin got two more stops added to it, a hypervisor console and a download manager, plus a small directory page to find them from. Along the way, a routing tool that updates one table but not the other, a bind mount forgotten twice in two days, and a redirect loop that turned out to be somebody else's nginx.
Reaching the media server from anywhere: a WireGuard bridge to the VPS
The media server has no public IP and lives behind a segmented internal network. A WireGuard tunnel, reused rather than duplicated, made it reachable at a real domain. A stale Docker bind mount nearly undid it on the very first deploy.
The media server that taught me infrastructure
It started because the gym WiFi was too slow to stream anything. One month, one dead SAN and two rebuilds later it had taught me Linux, Docker, ZFS, virtualization, VLANs and multicast, and none of the hardware was ever mine.
Timeline
Media server became a virtualization stack
A month of rebuilds: Synology to Ubuntu to Proxmox, ZFS RAIDZ1, VLAN segmentation and live IPTV over multicast - all on decommissioned hardware. Taught me self-hosting, which led to everything after it.
Read more