My Server Now Guilt-Trips Me Over Text. Here's the Build.
Photo: Антон Пищулин, CC BY-SA 4.0, via Wikimedia Commons
Every home lab has a dirty secret: you spend a weekend making everything redundant, monitored, and bulletproof — and then you stop looking at it. The dashboard goes unopened. The Grafana panels gather digital dust. Out of sight, out of uptime.
I hit that wall hard earlier this year. My little rack in the closet was fine, probably. Probably is not a monitoring strategy. So I did what any reasonable person with a spare Raspberry Pi and a Twilio trial account would do: I built an alert system that texts me. And then, because a plain "Disk usage 91%" wasn't going to get me off the couch, I made the messages dramatic. Petty, even. My server texts like an ex who has thoughts.
The Stack Is Boring On Purpose
Here's the whole thing:
- Uptime Kuma on the Pi, watching my services over the LAN
- Twilio for SMS, because I wanted texts to feel urgent in a way push notifications never do
- cron jobs running a handful of shell scripts for the stuff Uptime Kuma can't see — disk space, temperature, backup freshness
- A single Python script, maybe 80 lines, that formats everything into a message and fires it off
That's it. No Kubernetes, no message queue, no "observability platform." If this setup takes you more than an afternoon, you've over-engineered it, which is ironic given the subject matter.
The Pi itself is a 4B that was previously serving time as a retro gaming machine. It sips power, it's been rock solid, and if it dies, the irony of the monitor needing monitoring is a problem for future Aleks.
The Copy Is Not Boring At All
Here's where I deviated from every tutorial I read. The default alert tone of the entire monitoring industry is clinical: "WARNING: High CPU usage detected." Clinical doesn't motivate me at 10pm on a Tuesday. So I wrote a copywriting layer.
When disk space runs low on the media server:
"hey. so. /dev/sda1 is at 92% again. i'm not going to say it's the Plex library. but it's the Plex library."
When a service goes down:
"Jellyfin is down. third time this month. i've stopped taking it personally. have you?"
When the closet temperature sensor crosses 85°F:
"it's 86 degrees in here. i am a computer. i do not have sweat glands. please open a door."
Something about the tone shift works. A red banner in a dashboard is ambient noise. A text message written like a disappointed roommate demands a response. My median time-to-fix dropped from "whenever I next wander downstairs" to "within the hour."
I keep the templates in a YAML file so I can rotate them — partly for variety, partly because even a joke gets stale on the fortieth repetition.
What a Month of Alerts Taught Me
I expected the interesting part to be the build. It wasn't. The interesting part was the data.
Over four weeks, the system sent me 61 texts. Breaking it down:
- 31 were disk space warnings on one particular VPS I refuse to clean up
- 11 were temperature alerts, almost all during a July heat wave, almost all resolvable by leaving the closet door open
- 9 were a flaky smart plug rebooting my switch — the single most useful discovery of the month
- 10 were legitimate service outages, and 7 of those were caused by that same switch
So roughly 80% of my alerts traced back to two root causes. That's the real lesson hiding in this project: once you make failure visible, you discover your infra doesn't have fifty problems. It has two problems that have learned to cosplay as fifty.
I fixed the smart plug, deleted 40GB of docker images I'd been hoarding "just in case," and the alert volume dropped by two-thirds. The dramatic texts now arrive maybe twice a week, which is a sustainable relationship.
Rules I'd Give Past Me
If you're going to build something like this, a few guardrails from hard experience:
- Rate-limit everything. My first version texted me every five minutes during an outage. Twelve identical texts is not urgency, it's spam, and you'll mute the thread. One alert, then a follow-up only when state changes.
- Alert on symptoms you can act on. "CPU at 80%" made me anxious and changed nothing. "Backup hasn't run in 26 hours" sent me straight to the terminal.
- Keep the drama in the copy, not the thresholds. The triggers should be boring and conservative. The personality lives in the message, never in a 70%-CPU warning.
- Twilio costs real money per SMS. It's pennies, but a runaway loop at 3am is how you learn what "pennies" means at scale. Cap it.
Was It Worth It?
An afternoon of work, a Pi I already owned, and about $1.40 in Twilio credits for the month. In exchange: I fixed a hardware fault I didn't know existed, reclaimed disk I didn't know was full, and my closet server now has more emotional intelligence than most SaaS products I pay for.
The full scripts and the YAML of alert copy are in the lab repo — fair warning, the comments are also written in-character. Apparently I couldn't help myself.
Next experiment on the bench: teaching the system to send a single, smug "everything is fine, not that you asked" message on Sunday evenings. Morale matters, even for hardware.