·6 min read·Deep Dive

The Pi 5 in my closet runs four production-grade services

A Raspberry Pi 5 hosts home automation, a crypto trading harness, network monitoring, and a small FastAPI helper layer. They share more infrastructure than you'd think — and the patterns are the same ones that show up in production Cerberus Labs work.

By Igor Riera

There’s a Raspberry Pi 5 in my house running four production-grade services. None of them are toys.

It hosts my home automation platform: Home Assistant Supervised, controlling four Mitsubishi mini-split HVAC units, 14-plus Philips Hue lights, multi-room Snapcast audio, and devices including my pets’ feeding station through ESP32s. It hosts Cerberus Markets, a Kraken-backed semi-autonomous crypto trader currently running in paper-trade mode. It hosts a network monitoring stack: Pi-hole for DNS analytics, ntopng for WAN traffic visibility, Grafana stitching it together. And it serves a small FastAPI helper layer that the other services call for shared utilities — geocoding, time-zone calculations, a couple of API proxies that need credentials I don’t want sitting in three places.

These four things look unrelated at first glance. They share more infrastructure than you’d think.

Same host, same hardware

The Pi 5 is a 4GB model in an Argon ONE V3 case with an NVMe SSD. The case is the upgrade that matters most for this kind of load — passive aluminum heatsink wrapped around the SoC, an enclosed fan that’s nearly silent, NVMe via the case’s M.2 board over PCIe 3.0. The whole setup pulls under 12W at idle and peaks around 18W under combined load.

The OS is Raspberry Pi OS 64-bit (the Debian-derived one). Home Assistant Supervised runs in its own supervised setup. Everything else runs in Docker, except a couple of systemd services for things that don’t containerize cleanly (the ESPHome dashboard, primarily).

Same persistence pattern: append-only SQLite

Every service that records events writes to an append-only SQLite database with WAL mode enabled. Home automation state changes, Cerberus Markets signal and trade events, network monitoring samples, FastAPI request logs — all of it.

Append-only doesn’t mean “no updates ever.” It means the audit table is append-only. Each service typically has two kinds of storage: a slowly-changing reference table (devices, pairs, strategy configs) that gets normal CRUD, and an event log that’s strictly append. The current state of anything is a projection of the event log up to a given timestamp.

This is the pattern I keep coming back to. It costs almost nothing — SQLite handles millions of rows comfortably on a Pi 5 — and it pays off the first time you need to answer “what did the system actually decide at 3 AM last Tuesday and why?” The answer is in the log because the log is the record of decisions, not a side effect of them.

WAL mode is non-optional for this. Without WAL, concurrent readers and writers block each other in ways that surface as mysterious slow paths under modest load. With WAL, multiple readers can run while a writer holds the table — which is the access pattern almost every event-logging workload actually wants.

Same access layer: Tailscale

Four services, four dashboards, four reasons I want to reach them from my phone when I’m away from the house. None of them are reachable from the public internet. No open ports on my home router. No reverse proxy exposed to the world. Tailscale handles the entire access story.

Every device in my tailnet — the Pi 5, my laptops, my phone, my tablet — has a private WireGuard-based mesh address. Service URLs use those addresses. The Pi 5’s Tailscale address is the only thing my phone needs to know to reach Home Assistant, the trading dashboard, Grafana, or the FastAPI helper layer.

The properties this gives me, in roughly the order I appreciate them: a connection that comes up immediately when my phone joins any network because Tailscale handles NAT traversal silently, end-to-end encryption that doesn’t depend on me running a TLS proxy correctly, identity-based access (each device is authenticated, and I can revoke any of them in one click), and zero exposed surface — port scans of my home IP find nothing because nothing is listening on the public side.

The cost is that I have to install the Tailscale client on each device. That’s it. There is no “configure the firewall” step. There is no “renew the certificate” step. The service has been running on my devices for over a year and I have not touched its configuration in the last six months.

Same control-surface idiom: dashboard with a kill switch

Each service has a dashboard. Each dashboard answers two questions in its first viewport: “what is this thing doing right now” and “is everything healthy.” Each dashboard has a kill switch — a single button that stops the service from doing anything autonomous.

Home Assistant’s kill switch is a “vacation mode” automation that disables every automation triggered by sensors. The Cerberus Markets dashboard has an explicit global kill switch that prevents the executor from placing orders. The network monitoring stack’s “kill switch” is a Pi-hole pause button that disables DNS-level blocking when something on the network is misbehaving and I want to confirm it’s not a Pi-hole false positive.

The point isn’t that any of these will get pressed in normal operation. The point is that they exist, are reachable from my phone in two taps, and are obvious enough that I can find them at 2 AM without thinking. A control surface that requires you to remember a hostname or a CLI command at 2 AM is not a control surface.

Same operational discipline: idempotent retries

Every service that calls another over the network does so with idempotent retries. Home Assistant calls the CN105 control API on each mini-split: idempotent (setting target temp to 21 twice is the same as setting it once). Cerberus Markets calls the Kraken REST API for order placement: idempotent via Kraken’s clientOrderId parameter, which ensures a duplicate POST doesn’t produce a duplicate order. The network monitoring stack writes samples: idempotent via the (device, timestamp) unique key.

This is the discipline I keep relearning. Networks fail mid-request. APIs return 5xx for reasons that have nothing to do with you. Retries are necessary. Idempotency is what makes retries safe. Building idempotency into the design upfront costs almost nothing; bolting it on later is expensive.

Why this matters beyond personal infra

Cerberus Labs client work runs on Azure, on Heroku, in client-owned infrastructure. Different scale, different SLAs, different constraints. But the same five disciplines — append-only audit logs, narrow access surface, dashboard-as-control-surface, isolation by default, idempotent retries — show up in every production system I’ve ever shipped that didn’t blow up at 3 AM.

Personal infrastructure is where I refine the patterns under low-stakes conditions. Production work is where the stakes prove them. The Pi 5 in my closet is the cheapest lab I’ve ever run. The four services on it aren’t independent projects that happen to share hardware — they’re four implementations of the same five disciplines, which means every refinement to the patterns is one I can validate against four different workloads before I take it to a client.

If you only build for your day job, you stop encountering the kinds of constraints that force you to invent. If you only build for yourself, you stop encountering the kinds of stakes that force you to ship.