Home

My homelab: a small system to look after

Beyond software engineering, I enjoy running a small homelab focused on networking, infrastructure, and self-hosting. It’s a practical environment for working with topics such as Linux, storage, and monitoring, while learning about the challenges of keeping systems running.

It’s not a miniature data center or an attempt to self-host everything, I just focus on a few services that are useful to me. That gives me something concrete to maintain, without needing a large setup to make it interesting.

What it does

At home, I have a Dell OptiPlex 5070 Micro running Debian 13 as the main server. Its two main jobs are DNS filtering and media streaming.

For DNS, I use Blocky with Unbound to resolve queries and filter unwanted domains. For media, I run Jellyfin in Docker, with the library on a separate 1 TB SanDisk portable SSD. Both serve a practical purpose and have a few users, which makes the lab more engaging than infrastructure I would build for an exercise and then switch off.

A CyberPower CP900EPFCLCD UPS supports core network equipment such as the ONT and router, and the main server. It keeps them powered during short outages, preserving internet access as long as the ISP connection remains available. A USB connection lets the server monitor the UPS through Network UPS Tools and shut down cleanly if the battery runs low. Power is a primary dependency, even when everything above it is configured correctly.

Outside the home network and in another region, I have a small VPS also running Debian 13. It hosts the monitoring system and gives me a separate place outside my home premises from which to check whether services are reachable.

Tailscale connects all devices and provides private links between them. Additionally, it allows mobile devices connected to the tailnet to use the home DNS resolver beyond the home network, and external users to access Jellyfin. ACL rules define which devices/users can reach which services.

Knowing whether things actually work

The monitoring system has an agentic side built around Hermes, an AI agent running on the VPS. It runs schedulded routine health checks, processes them, and sends me alerts and reports through Telegram. I can also ask it questions interactively about the existing metrics, logs, and the state of the system. I think of it as another observability interface: alongside exploring dashboards in Grafana, I can have a conversation with an agent that checks how things are doing.

Underneath both interfaces is the same monitoring data. Grafana Alloy collects host metrics and logs from both servers and their running services, DNS stats, drive health, and UPS status, and sends them to Prometheus and Loki on the VPS: Prometheus stores the metrics, and Loki stores the logs.

A running process doesn’t necessarily mean a working service. For DNS, I check that queries resolve and unwanted domains are blocked. For Jellyfin, an HTTP response isn’t enough: the media disk also needs to be mounted and readable. The checks verify both, and startup guards prevent situations such as Jellyfin launching with an empty mount directory.

Another question is what happens when monitoring itself fails. An external heartbeat service watches for missing signals from the VPS and its check scheduler. The VPS also uses DNS independently of the home server, so losing DNS at home doesn’t by itself prevent an alert from being sent.

These are small details, but they’re the part I find most interesting: deciding what a check actually proves, and what it depends on to reach that conclusion.

The trade-offs are part of the project

Running useful services at home means incidents have consequences. If the local DNS server stops working, ordinary internet use is affected even if the connection itself is fine. If Jellyfin is not reachable, users will complain. That makes recovery instructions more valuable than another dashboard.

I keep setup scripts, container definitions, network policy, and dashboards in version control. I also document failure scenarios and recovery steps. Not everything is automated: some service configuration and backups still involve manual work. Keeping that distinction explicit matters more than pretending the repository can rebuild everything unattended.

There are also shortcomings I haven’t tackled yet. DNS still depends on one local machine, and the media library currently lives on a single drive. Drive monitoring and a UPS reduce some risks, but neither replaces a backup. Improving that is more useful than adding another service just to expand the stack.

For a personal setup, I don’t need to eliminate every single point of failure. I do need to understand the consequences and decide which ones are worth addressing.

How it connects to software engineering

The overlap with my usual work is less about using the same tools and more about asking the same questions. What does this service depend on? How do I distinguish a failed dependency from a failed application? What information will I need when something stops working? How will I recover it?

The homelab makes those questions tangible. A container restart won’t reconnect a disk. A healthy HTTP endpoint doesn’t prove the service is usable. A monitoring system can’t report an outage if it relies on the component that just failed.

I wouldn’t take this setup as a template for a serious workload, but it’s a useful place to practise thinking beyond the code and following a request through the system around it.

That’s what I like about it as an adjacent interest: I get to work with hardware and services I use, make decisions about reliability and maintenance, and see how those decisions hold up. The goal isn’t to keep adding infrastructure. It’s to have a small system that earns the time I spend looking after it.