When I was a teenager in Peru, I ran a home media server. Plex, a hard drive, and a lot of files whose origins I’d rather not go into. Not my proudest setup. But a Peruvian family budget and a US price list didn’t leave many options, and nobody around me thought twice about it. In Lima you could buy almost any movie on DVD at the market, often before it reached the cinemas, for a few soles.
Then Netflix arrived. One fair price, almost everything in one place, on any screen. I deleted the whole setup and paid, happily, for years. It turned out most people didn’t want to pirate. They wanted a decent service at a price they could pay.
Then it got worse, one email at a time
That was the good part. Since then:
- The price keeps going up. Every service, every year or so, with a polite email about all the value they’ve added.
- The catalog split up. Every studio wanted its own app, so what used to be in one place is now spread across five subscriptions.
- Ads came back. First as a cheaper plan. Then on the plan I was already paying for, unless I paid a bit more to make them go away.
- Things disappear. Shows leave with a month’s notice, or none. Some were pulled from a service entirely, a few of them for the tax write-off.
- Sharing became suspicious. My account is now very interested in which Wi-Fi I’m on.
The deal I switched for doesn’t exist anymore. I’m paying more, for less, to watch ads. There’s a word for this now: enshittification.
So I’m back to a server at home. The difference is what goes on it: Blu-rays I bought, things that are free to watch, and the audiobooks I already paid for on Audible. Same idea as back then, minus the market DVDs.
The push came from Brandon Hopkins and TechHut, a YouTube channel about Linux, self-hosting and homelabs. A lot of my stack started as something I saw there, and the TechHut homelab repo is where I kept going back for the details. Go watch it. Interesting stuff, and dangerous for your free time.
It took three days, part-time, from a used mini PC to a box that streams, finds new episodes, watches itself and backs itself up. Claude Code did the typing, over SSH from my Mac. I made the decisions and did everything that needed hands, which turned out to be the hard part.
The box
A used Dell OptiPlex 7070 Micro from Kleinanzeigen, the German classifieds, for €130: an i5-9500T, 8 GB of RAM and a 256 GB SSD. The listing said 7060 with an i5-9500. It wasn’t. Same graphics chip and a quieter fan, so I let it slide. Used-PC listings lie, but sometimes in your favor.
The graphics chip is the point. Intel’s Quick Sync converts video on the fly for whatever screen is asking, so an old smart TV doesn’t need to understand every format. On this box it turns 1080p HEVC into H.264 at about 200 frames per second, eight times faster than anyone can watch it. What it can’t decode is AV1, so the library prefers other formats and only takes AV1 when there’s nothing else.
This is the model, in Dell’s own marketing photo. Mine has fewer pencils.
Most of the time, it’s bored:
What runs on it
Proxmox at the bottom, and two containers on top:
- DNS. Pi-hole and Unbound, in a container of their own. Ads are blocked for every device in the house, and I can break the media side without anyone asking why the internet is down.
- Everything else. One Docker Compose file. Jellyfin to watch. Seerr to ask for things. Radarr, Sonarr, Prowlarr and Bazarr to find them, file them and get subtitles in English, Spanish and German. qBittorrent for downloads. BookOrbit for ebooks and audiobooks, with Libation pulling in my Audible purchases from three different stores. Caddy for HTTPS, and Beszel and Uptime Kuma watching all of it.
Seerr is the part my family will see: a catalog where they can browse and ask for something, and I take it from there.
Who gets in
Every app has its own name, like media.hchome.stream, with a real certificate. The DNS record points to a private address, and the certificate comes through a DNS challenge, so there isn’t a single open port on my router. From the internet, the box doesn’t exist.
Outside the house, there’s Tailscale. It puts my phone, my laptop and, soon, my family’s TVs on a private network of their own, encrypted end to end with WireGuard. A device has to sign in to join, and an access policy decides who reaches what. (The policy is a file in the repo, like everything else.) It also works behind my provider’s DS-Lite setup, which doesn’t give me a public IPv4 address to open a port on, even if I wanted to. I tested it from my phone on mobile data and it worked the first time, which never happens.
Downloads go out through NordVPN. Peer-to-peer shows your IP address to everyone you connect to, and I’d rather it show NordVPN’s. Gluetun runs the tunnel, and qBittorrent doesn’t get a network of its own: it uses Gluetun’s. If the tunnel drops, qBittorrent has no network at all, so my home address never leaks out by accident. The agent tested that by stopping the tunnel and watching every connection die.
How the agent and I split it
- A plan before the hardware. The first session, before the box even arrived, wrote
docs/PLAN.md: architecture, decisions, network, storage, and phases with checkboxes. It got updated every time reality disagreed, which was often. Across several sessions, it was the shared memory. - Scripts, not clicks. Every setting in every web UI became an API call in a script that can run again. About 45 commits in three days, and
git logdoubles as a build diary. Rebuilding the box means running the scripts. - Secrets never went through the chat. VPN keys, tokens and passwords went into a small helper that asks for them on the server and writes them straight into its
.env. The agent never saw them. At one point I suggested just committing them to the private repo. The agent talked me out of it. Fair. - I said yes to anything irreversible. Router changes, account settings, deleting files. Each one waited for an explicit yes.
- Breaking things on purpose. The agent stopped the VPN, killed apps and waited for the alerts. That’s how the first real bug showed up before it bit.
Booting a PC without a keyboard
The PC arrived a day before the keyboard. Waiting a day was apparently not an option.
The plan was to boot the Proxmox installer from a USB stick. The BIOS was set to the fastest boot mode, which skips USB devices, so the stick never showed up. Windows can ask the PC to boot into its BIOS settings, but with no keyboard attached, the BIOS ignored that too. Windows’ advanced startup was greyed out, and only in German.
What worked was Dell’s own BIOS tool for Windows, Dell Command | Configure. It needed a missing Visual C++ runtime first, naturally. Then one line:
cctk --Fastboot=ThoroughAfter a restart, Windows offered to boot from the USB stick. The agent had already built an installer with an answer file, so Proxmox installed itself without a single keypress. (That stick wipes any PC that boots from it. It’s labeled now.)
The keyboard arrived the next day. I used it to change three BIOS settings, confirmed the box boots without it, and unplugged it again.
Running is not working
The stack was up by the end of day one. Day two was finding out what “up” means.
- Downloads that never started. After a reboot, qBittorrent came up one second before the VPN tunnel. It was running, with zero peers and no error anywhere. The fix is a tiny watchdog container: every five minutes it checks whether qBittorrent is older than the tunnel and whether it can see any peers, restarts it if not, and sends my phone a message through ntfy. Tested by restarting the VPN on purpose. It recovered within five minutes.
- A miniseries that weighed 51 GB. The default quality profiles assume you own a big NAS. I have 141 GB. Now they prefer x265, which is about half the size, cap the size per minute (about 1.6 GB per episode and 6 GB per movie), and skip remuxes. The same miniseries now takes 6.5 GB.
- “Temperature above 85 °C.” It wasn’t the CPU, which peaked at 58 °C. It was the SSD controller, which always runs hot under heavy writes. The drive itself was fine. That one sensor no longer alerts.
- Audiobooks. 53 of them, about 1,270 hours, and a quarter of the disk. I went through the list by size and kept seven. The disk went from tight to 74 GB free.
Uptime Kuma checks each of the 14 apps and sends my phone a message when one goes down and another when it’s back. FlareSolverr’s 95.15% is the test where I stopped it on purpose.
And this is what that looks like on my phone: the SSD controller’s 85 °C scare, and FlareSolverr going down and coming back during the test.
And BookOrbit, after the pruning. The seven survivors, plus Pride and Prejudice to test ebooks:
Backups cover the containers, not the media: apps, settings and databases, every night at 3 a.m., keeping the last four. About 6 GB a night. Movies I can find again. My watch history I can’t.
The first backup failed. The containers run unprivileged, so they couldn’t write to the locked-down backup folder, and the fix was one line telling Proxmox to stage the backup in /var/tmp instead. The next run went through:
Still on the list
- A new router. My provider’s router hands out its own DNS over IPv6, so half the house quietly skips Pi-hole. A refurbished FRITZ!Box is on its way. The agent figured that out by reading the router’s admin page through Chrome. I’m the one who’ll have to plug it in.
- A bigger SSD. Used, 2 TB, PCIe 3.0, because the slot can’t go faster and I’m not paying for speed it can’t use.
- The family rollout. Three people ten minutes away who are, in their words, “not so tech”. A TV box with Tailscale and Jellyfin, and a one-page guide.
- An outside check. Everything that monitors the box runs on the box. If it dies, it dies quietly.
What I take from it
Three days is fast for something this size, and the agent is why. It wrote every config, every script and every commit, and it tested things I would have assumed worked. The decisions stayed mine: what goes on the box, what gets deleted, and when the router gets touched. So did the USB stick, the BIOS and the keyboard I didn’t have.
As a teenager, I ran a media server because I couldn’t pay. Now I run one because paying stopped being enough. The streaming services still have the bigger catalogs. Mine has no ads, no price increase email, and a support team that answers right away. It’s me.