Media stack: a self-hosted library platform and its network
About thirty containers serving a friends-and-family media library, the network decisions behind it, and the fault each one closed.
Docker Compose network segmentation DNS resolution and filtering reverse proxy observability runbook discipline
The short version
A self-hosted media platform: about thirty containers I assembled and operate on one Docker Compose host, serving friends and family behind an ingress with no inbound ports open at home. It is evidence that I can integrate third-party services I did not write into a single running system: container orchestration, network segmentation, split-horizon DNS, a reverse-proxy TLS boundary, and network-namespace isolation. That is platform and operations work: assembling other people's software into one reliable system, isolating faults, and writing down every decision so the next failure is a lookup, not a chase.
The platform
A Plex media server runs on the Hyper-V host and serves the library to friends and family behind capability-tiered access, managed through the friend-onboarding front door. The automation around it runs as about thirty containers under Docker Compose in an Ubuntu VM on the same host, every configuration file tracked in git. Those containers cover requests, indexing, the dashboard, DNS, and the reverse proxy; the applications are third-party, and my work is assembling them into one coherent system and keeping it running.
The platform handles six functions.
- Media serving, with a separate activity monitor for usage and stream health.
- Self-service request and fulfillment. Users request titles through a web interface or a Discord slash-command bot, and the platform processes them automatically.
- A unified dashboard giving operational visibility across every service from one screen.
- Nightly automatic image updates, with pinned exceptions for stability-sensitive containers and a notification when something changes.
- A network-wide DNS resolver and ad-blocking layer, on its own IP directly on the physical NIC.
- Internal name resolution, answering every service under a
.landomain through the reverse proxy.
Networking decisions and the faults they closed
The network exists to solve three problems: isolating services, keeping the resolver reachable, and undoing a host-level lock that had broken container networking. Every row below began as a fault I chased down, then wrote into the runbook so I would not chase it twice.
| Decision | Problem solved | Consequence accepted |
|---|---|---|
| Two container networks: a bridge for services, a macvlan for the DNS resolver | The resolver needed its own MAC and IP on the physical NIC, so LAN clients reach it directly and it bypasses the host firewall. | The host cannot reach the resolver directly; traffic leaves the host, hits the physical switch, and comes back. The runbook says so. |
Internal names answer under .lan, served by the reverse proxy |
Internal hostnames cannot get public TLS certificates, and every service needs one entry point. | Internal traffic stays HTTP inside the LAN trust boundary; anything from outside terminates TLS at the cloud front door. |
| A remote-access subnet router on the Hyper-V host publishes the LAN to a private overlay network | Authorized devices outside the house need LAN reachability without opening any inbound port. | The host carries an extra routing table, maintained as part of the lab topology. |
| The one service whose traffic must stay in its own tunnel runs in a separate container and network namespace, with the lock scoped to it | A host-level lock had blocked every bridge container from reaching the host and the LAN. | The lock still exists, scoped to that namespace, so that service can only exit the way the lock allows. Every other container reaches the host, LAN, and internet normally. |
| Never mount a database-backed service's config on SMB | SQLite write-ahead locking breaks over CIFS and can corrupt the database. | Database configs live on local disk only, as a hard rule. |
| Two DNS resolvers: a LAN macvlan primary and a cloud secondary | Ad-blocking and resolution stay available for both on-site and remote clients. | A nightly sync keeps the two resolver lists consistent. |
The bridge-to-host routing fault cost me hours across different network modes before I isolated the cause. It went into the runbook so the next person, including future me, finds it in a search instead of a chase.
Storage and operations
The library sits on about 46 TB of spinning disk on the Hyper-V host. An all-SSD working tier on the training host handles ingest staging. A 12 TB drive docked at my desk holds cold-swap copies of what I choose to keep twice; it is not a mirror, and the array is not mirrored. Separating ingest from archive cleared a throughput bottleneck: content lands on SSD first, then migrates to spinning disk for long-term storage.
Operations run through chat-driven controls on my local assistant, with confirm-on-write safety, described on the ops agent page. Every reload and restart procedure lives in the runbook. A quality-profile sync tool for the media apps runs manually with a dry-run pass first, because it can reset hand-tuned scoring. The cloud secondary resolver syncs nightly, and the energy page covers what running this continuously costs.
The multi-site era, and why it ended
Through 2025 the platform ran from three physical sites at once: my home site and two remote locations, linked by a private overlay network and administered remotely. It followed a single-writer, multi-reader pattern. The request-and-fulfillment tooling was a master at the home site, and the remote sites read from it instead of keeping their own lists, so library state stayed consistent without replication drift.
It existed because the home connection was capped at 40 Mbps upload; serving streams from remote sites with better uplinks gave acceptable quality without transcoding everything down. It ended when the home site moved to 2 Gbps symmetric fiber, which removed the rationale. The service consolidated to one site behind the owned cloud front door, and one remote location still runs ad-blocking only.
The consolidation rebuild (hardware)
Consolidation meant a component-level rebuild of two machines, and the order mattered because the second was built partly from parts the first gave up. I upgraded the training host in place, keeping its CPU, motherboard, and storage, and fitting a new 24 GB GPU, 32 GB of memory, and a new case. I pulled the Hyper-V host out of a remote site, brought it home, and rebuilt it on a new motherboard and CPU with the training host's old GPU, memory, and case. It kept its name and role with almost none of its original parts. The sequence was removal, transport, install, then verify boot and networking. I did not call the migration done until every service came back healthy on the refreshed VM.
The skills sheet maps these operations to role requirements: container operations, network troubleshooting, fault isolation, hardware deployment, and documentation discipline.