Infrastructure

How this all runs

Nobody pays me to do this. It's three machines I own and look after myself: a home PC, a home server, and one VPS. Everything on this page is something I set up myself, broke at least once, and eventually got working, which is a slower way to learn than a course but it does stick. Here's the full chain, front to back.

🗺️ How it actually connects

This is the real shape after moving to the smaller VPS in August 2026, not the old game-server diagram and not a wishlist. Hover any node to trace its connections. Solid lines are traffic between things I run; dashed lines cross a third-party service or the locked-down tunnel between home and the VPS.

Cloudflare handles public DNS and protects the main site, whose origin now refuses direct requests. The Debian 13 VPS accepts HTTPS through Caddy, which serves the static files and routes the site's small APIs. Hilray TV is deliberately different: Jellyfin stays on the home PC, and that PC opens an outbound reverse SSH tunnel to a Unix socket on the VPS. Caddy can reach the socket, but the VPS gets no general route into my home network. Hazel stays local and uses its own separate deployment key when I ask it to publish the site.

🖥️ See it live

A real request, not a canned animation. This calls /api/status live in your browser and separates "the endpoint answered" from "the retired game workload is running." The first should be true. The second is intentionally false.

13
Debian on the VPS
2
Public services
2
Purpose-specific SSH keys
1
Caddy reverse proxy
3
Expected public ports
0
Game servers running

🖥️ My home PC

⚙️

The specs

My main rig, which does triple duty: gaming, streaming, and hosting whatever local AI I'm running that week.

Ryzen 7 5700X3DRX 9070 XT 64GB RAM1TB NVMe2TB HDD backupJellyfin
🦙

Local AI models

I run local models through Ollama, including Qwen3.6 30B and Fable5, so I get useful output without any of it leaving my machine. That's the whole of the AI story here: local models, run on hardware I own, for my own use. Nothing on this page calls out to a hosted model.

OllamaQwen3.6 30BFable5Local only

🗄️ My home server

🗄️

The quiet workhorse

A separate box from my main PC, dedicated to whatever needs to run 24/7 without tying up my gaming rig. Keeping the always-on things off the machine I use every day means I can reboot one without taking down the other. Most of what it does now lives in virtual machines, including the ones that sit in a Discord call all evening and stream a show to whoever's watching along.

Intel Core i732GB RAM500GB NVMeVMs24/7
🎬

Hilray TV, and why it stays home

Jellyfin runs on my home PC, not on the VPS and not on this server. The library stays beside the drives that hold it. Remote viewing crosses an outbound reverse SSH tunnel to the VPS, so there is no forwarded Jellyfin port and no reason for the public internet to know where the house is.

Watching together in Discord is still a separate thing: the VMs on the home server can stream into a call. Hilray is the browser library; the VMs are the shared couch. Similar outcome, completely different route.

JellyfinHilray TVReverse SSHRuns on the home PC
🎮

Steam Deck

My portable gaming rig, running SteamOS. It's the third piece of "my own gear," alongside the PC and the home server.

SteamOSPortable

📡 Network

📶

Eero mesh

Whole-home mesh WiFi so nothing in the house has a dead zone. Every device, from the home server down to the Steam Deck, stays connected on the same network without me thinking about it.

EeroMesh WiFi

Fiber

1 gig symmetric, unlimited data. No bottleneck between my gear and the outside world.

1 Gig Up/DownUnlimited Data

🏡 The rest of the house

Not everything on the network is a server. The consumer half of the house sits on the same Eero mesh as everything else, and while it's the least glamorous part of the setup, it's probably the part I'd miss most if it vanished overnight.

📹

A few cameras

A small camera setup around the place. These are cloud-dependent, not self-hosted, which makes them an honest exception to the rest of this page. I'd like to replace them with something local eventually, and I'd rather flag that here than quietly leave it out. I'm not going to name the brand or say what they point at, for reasons I hope are obvious.

Cloud, for nowSecurity
🗣️

Google Home

Voice control for lights, timers and music, and the fastest path to "turn everything off" when I'm already in bed and don't want to get back up. Ties into the automation layer below.

Google HomeVoice
💡

Govee lights

The purple. Govee strips and bulbs are where the whole site's color scheme comes from: the room looked like this first, and the CSS followed.

GoveeRGB
📱

Android

My phone is the delivery endpoint for most of this. Hazel's self-care and medication reminders land here, the cameras and lights are controlled from here, and it sits on the same Eero mesh as everything else in the diagram above. All the infrastructure further up this page exists so that something useful shows up on this screen at the right moment.

AndroidEeroHazel reminders

🤖 Automation

The Hazel Command Centre ties most of this together. It controls the bot, kicks off site deploys, and sits alongside a few other tools that handle the smart-home and day-to-day side of things. Worth stating plainly, since a lot of setups like this quietly assume otherwise: there's no AI anywhere in this automation. It's scheduled jobs, scripts and APIs. The only models I run at all are local ones through Ollama, on my own machine, and nothing here sends anything to a hosted model.

Hazel Command CentreOpenClawGoogle HomeAndroidNo hosted AI

🐳 Running on my VPS

The new box is deliberately boring and small. I cancelled the bigger game-server VPS because paying for idle ambition is still paying for idle ambition. Debian 13, Docker, Caddy, the website, its tiny APIs, and the far end of the Hilray tunnel are the whole job now. No Minecraft, no game world quietly eating RAM, no pretending I need headroom for a maybe.

🌐

lilharper.dev

The actual site: hand-written HTML, CSS and JavaScript, served by Caddy from a Docker-mounted directory. Hazel publishes it with one button, but the files remain readable and boring on purpose.

Static filesCaddyCloudflare
🧩

The site's small APIs

Blog posts, guestbook entries, reactions, the question inbox and status data live behind the same Caddy front door. The container is read-only except for its little JSON data directory, runs without extra capabilities, and has no route to Docker itself.

PythonRead-only containerNo Docker socket
📺

Hilray's public doorway

Caddy terminates HTTPS for watch.lilharper.dev and proxies into a private Unix socket. The socket only exists while the home PC's reverse tunnel is alive. No tunnel means a clean failure, not a surprise fallback.

HTTPSUnix socketFail closed
🗃️

The game-server archive

Hytale, Enshrouded and Palworld are not running on this VPS. Their pages remain as honest project history and the live indicators say offline. I learned too much building them to delete the evidence just because the monthly bill won.

Not runningKept as historyNo Minecraft

The boring security layer is real now. SSH is key-only, root login is disabled, Hilray's key is forced into its tunnel-only job, the host firewall allows only SSH and web traffic, multicast name discovery is off, Debian installs security updates automatically, and the public web containers run read-only with dropped capabilities.

🏠 Running on the home network (not the VPS)

These are all live and in daily use. They just live on hardware in my house and not on the VPS. Either they're not ready to move, or moving them would defeat the point.

🐺

Hazel

Personal assistant and community management bot. The bot process runs on my home server and ties Android, the PC, and Discord together for reminders and community management. No model of any kind is involved: it's scheduled logic and API calls. It moves to the VPS once it's ready, not before.

Pythondiscord.pyLive since 2024
📺

Hilray TV

Jellyfin and the actual library live on the home PC. Hilray's SSH key is restricted server-side to the tunnel health check, stale-socket cleanup and forwarding. It cannot open a shell or run admin commands; Hazel's separate key handles deployment and administration.

JellyfinRestricted keyOutbound tunnel
🌙

Hazel Care

Shipped and in daily use, fully local, and it stays that way by design. It's a desktop app, not a service, so there's nothing here to migrate to the VPS and nothing gained by trying. Full deep-dive →

PythonLocal-onlyShipped

💽 Storage & media

Every drive listed further up this page, added together in one place. This is what I actually own, not a capacity plan and not a wishlist.

3.5TB
Local storage, total
1.5TB
NVMe, PC + server
2TB
HDD, backups only
3
Drives, 2 machines
1
Hilray library, on the PC
2
Streams at once, in Discord

Those totals are just the drives already named above: a 1TB NVMe and a 2TB HDD in the home PC, plus a 500GB NVMe in the home server. What I'm deliberately not putting a number on is the library itself. I don't track how big it is, how many hours are in it, how much free space is left on any of those drives, or how close the HDD is to dying of old age. When I measure any of that, it goes here. Until then I'd rather leave the gap visible than fill it with a number I made up.

💾 Costs & backups

The old version of this page treated a bigger bill like a flex. It wasn't. The useful number is whether I am paying for what I actually use, so the current setup is intentionally smaller and the precise monthly price stays off this page until I have the invoice in front of me.

1
Lower-tier VPS
$10
Domain /yr
1
Local VPS restore archive
2
Separate VPS key roles
1
Home PC doing the heavy lifting
2TB
Local backup drive
0
Cloud backup services

The VPS has deploy-time snapshots, and the migration archive also lives on my PC. Hazel and Hazel Care still have local backups. That is better than one copy and still not a real off-site strategy: the house remains the shared failure domain. I know the gap is there, so it stays written down instead of being rounded into "backups handled." More on the rollback tooling →

So is self-hosting cheaper? It is now, because I finally let the answer change the architecture. The game-server box was fun and taught me a lot, but keeping it for imagined future load was expensive theatre. The smaller VPS holds the things I actually use from outside the house, while the media stays on hardware I already own.

None of this counts electricity or my own time. When it breaks at 2am, I'm still the entire support team. What the money buys is not a bargain so much as control: I know where every process runs, I can open the box when it misbehaves, and now I am no longer paying extra just to prove I could fill it.