HOMELAB MEDIA
A single Docker host that requests, organizes, compresses, and streams a personal media library, reachable from anywhere through a Cloudflare Tunnel with no open ports.
Hands-off operation: cron jobs throttle searches, a watchdog restarts a stalled service, and a Slack digest reports health, usage, and weekly activity.
// problem
Run a full media pipeline on one box without exposing the home network, without babysitting stuck jobs, and without filling the disks.
// limitations
- One host, consumer hardware.
- 1080p playback target for remote viewers.
- Every secret lives in an untracked .env. The repo holds only placeholders.
// infrastructure used
- Docker Compose stack split into a main services file and a separate playback stack.
- Outbound-only Cloudflare Tunnel publishes the two public apps. No inbound firewall rules.
- Download traffic is pinned inside a WireGuard VPN network namespace.
- Tdarr flows convert the library to HEVC on Intel Quick Sync, with worker limits by time of day.
- A strict HAProxy socket proxy limits which Docker API calls the health tooling can make.
// lessons learned
- 01A VM disk was on a failing USB stick.
Checksum errors appeared before anything visibly broke. I copied the disk to NVMe, zero-filled the bad blocks, and now monitor storage health instead of assuming it.
- 02Hardware transcoding vanished after a rebuild.
The transcoder lost its GPU device, so every HEVC job failed quietly. A test encode is now part of checking any rebuild.
- 03A one-letter typo in an environment variable.
The variable was empty, so a container got a random IP instead of its planned one. I now render the final Compose config before deploying.
- 04Files with no extension stalled imports.
A post-processing script now adds the right extension from the file contents. Its first setup loaded but never ran, so I verify that hooks fire, not just that they load.
- 05Removing a network client turned off IP forwarding.
All container networking dropped for two minutes. Kernel settings the stack depends on are now pinned.