vibescoder

Smart Home, Dumb Luck, Episode 4: The Backup Plan That Would Have Silently Failed

·8 min read

Episode 3 ended with nine (now ten) Uptime Kuma monitors watching the homelab and one honest admission: everything left in the plan is blocked on standing in my actual house with a Zigbee dongle in hand. That’s still true. This episode doesn’t touch Zigbee either. Instead it’s the session where I asked for something smaller, “let the agent actually manage Kuma, not just read it,” and pulling that one thread unraveled a security gap worth closing, a backup plan worth test-driving, and a Wake-on-LAN assumption that was quietly wrong.

None of it needed me to leave my desk. All of it needed me to actually check, rather than assume.

Read-Only Wasn’t Going to Cut It

The plan was simple: give the agent an Uptime Kuma API key so it could check monitor status without me relaying screenshots. Except Kuma doesn’t have a REST API for monitor management at all — not a limited one, none. The only API-key-protected REST endpoint is /metrics. Everything else, adding a monitor, editing a threshold, pausing something, runs over Kuma’s internal Socket.IO channel, and the uptime-kuma-api Python client is the only practical way in from outside the browser.

That client needs a real username and password, not an API key. Which changed the actual decision: since I’d already said I wanted the agent adding and tuning monitors going forward, not just reading them, the read-only API key I was about to set up would have been redundant work. Username and password covers both read and write in one credential. So that’s what we did instead.

Retiring the Last Manually-Copied Secret File

Here’s where Episode 3’s near-miss comes back around. That session almost committed a live secret to git in plain text, caught and scrubbed within minutes, with a promise to keep secrets in local workspace files referenced only by path from then on. That was the right instinct for that night, but it’s the same pattern this blog already moved past in June: a file that lives on one workspace’s disk doesn’t exist on the next one. Every fresh workspace would need the same credentials handed to it by hand, forever.

So the four credentials still living that old way, the Kuma login, the ha-mcp webhook connect URL, a Proxmox monitoring token, and the Discord alert webhook, all became Coder User Secrets instead: file-target secrets injected automatically into every workspace at boot, the same mechanism already handling the ThinkCentre’s SSH key and the Home Assistant long-lived token.

The migration hit one genuinely confusing snag. coder secret create‘s --file flag isn’t “read the value from this file,” it’s “write the value to this path inside every workspace.” The value itself always comes from --value or stdin. Get that backwards and the CLI fails with an error that doesn’t obviously point at the fix: secret value must be provided with --value or stdin via pipe or redirect. Once that clicked, the actual commands were simple, read -s to prompt without echoing, pipe straight into coder secret create, done.

A Token Rotation That Didn’t Have to Be a Guess

One of those four credentials, a read-only Proxmox API token for future Kuma monitors, had a problem: nobody had the actual secret value written down anywhere. Proxmox only shows a token’s value once, at creation. The honest options were delete-and-recreate it, or leave the gap.

Before touching anything in the Proxmox UI, the real question was whether deleting it would break something already depending on it. That’s not a guess worth making from memory. A grep across every kuma-* push script and the Proxmox host’s own crontab confirmed the answer directly: nothing referenced that token at all. The one monitor that looks like it should use it, HAOS VM memory pressure, actually authenticates locally as root via pvesh on the Proxmox host itself, no token involved. The token had been created for “whenever a future monitor needs to hit the API directly” and just never got used. Safe to delete and recreate, confirmed rather than assumed.

Standing Up Proxmox Backup Server, Carefully

The plan has called for Proxmox Backup Server since the very first draft: incremental, deduplicated backups of the Home Assistant VM and the Kuma/AdGuard containers, targeting the AI workstation’s spare NVMe capacity over Tailscale instead of an unspecified NAS. Actually building it raised a question worth answering before touching that machine at all: it also happens to be the Docker host running this blog’s own Coder control plane. Nothing installed there should risk it.

Proxmox Backup Server only officially targets Debian, and the workstation runs Ubuntu 24.04. Forcing Proxmox’s Debian-built .deb package onto a different distro’s dependency tree, on a box that can’t afford to break, wasn’t worth the risk. A Docker container solved it instead: the same official proxmox-backup-server package, installed inside a clean debian:bookworm-slim base image, isolated from the host’s own packages and trivially removable if anything about it turned out wrong.

The first build looked fine and wasn’t. It came in at a suspiciously small 130MB, because the cleanup step at the end of the Dockerfile, apt-get purge curl gnupg && apt-get autoremove, doesn’t stop at removing curl and gnupg’s own leftover dependencies. autoremove cascades to anything currently marked as automatically installed, and it swept up proxmox-backup-server itself along with real runtime dependencies like lvm2 and smartmontools. The build exited 0. The image just didn’t contain the thing it was supposed to install. Only checking dpkg -l inside the built image, and confirming the actual daemon binaries existed on disk, caught it. The fix was to stop being clever about cleanup and just clear the apt cache. Correct image: 552MB, verified for real this time.

The second snag was smaller but stranger: the workstation’s own DNS resolution timed out specifically on Proxmox’s CDN hostnames, enterprise.proxmox.com and download.proxmox.com, while everything else on the internet resolved fine. Both hostnames answered instantly through a public resolver instead. Rather than touch this box’s DNS configuration, or restart its Docker daemon, which would have bounced the Coder control plane and every workspace running on it, the fix was scoped to just the one build: docker build --add-host pointing those two hostnames at the IP a public resolver already gave back.

The Backup That Would Have Silently Failed

The last piece was the one worth writing about even though nothing got deployed. Sizing and testing the container raised an obvious question: the AI workstation isn’t always on. So how does a scheduled backup job, or Kuma’s own health check for that matter, guarantee the machine is actually awake when it needs it?

The plan already had an answer, sort of. Wake-on-LAN is already wired up as a one-tap button in Home Assistant, and Episode 3 explicitly queued the idea of pausing and resuming a Kuma monitor off that same wake signal. Checking whether that same mechanism would actually reach a scheduled Proxmox Backup Server job turned up something the plan hadn’t accounted for: the AI workstation sits on one subnet, 192.168.0.0/24, and the ThinkCentre running Home Assistant sits on a completely different one, 192.168.86.0/24. A Wake-on-LAN magic packet is fundamentally a same-segment Ethernet broadcast. It doesn’t cross routers between separate subnets without a relay or explicit forwarding configuration, and this setup has neither.

That would have meant building an entire wake-before-backup automation on top of a wake signal that couldn’t physically arrive. It resolves itself for free once the ThinkCentre makes its planned move to the permanent home network and lands on the same LAN as the workstation, one more reason that move handles more of this plan than it looks like on paper. Until then, both the Wake-on-LAN button flow and any scheduled backup job stay queued rather than built on an assumption nobody had actually tested.

What’s Next

Every real milestone left, Zigbee pairing, decommissioning SmartThings, wiring the Wake-on-LAN flow so it can actually reach the workstation, deploying this now-verified backup container for real, is bundled into the same move-day pass. That’s by design: none of it is safe to test remotely, and now none of it is blocked on an untested assumption either.

By the Numbers

  • 0 REST API endpoints Uptime Kuma exposes for monitor management, confirmed directly rather than assumed
  • 1 CLI flag (--file) that means the opposite of what it sounds like it means
  • 4 credentials moved off manually-copied files and onto real Coder Secrets
  • 1 Proxmox API token deleted and recreated, confirmed safe first via grep, not memory
  • 130MB → 552MB the actual size difference between a Docker image that looked done and one that was
  • 2 hostnames that failed to resolve locally but answered instantly through a public resolver
  • 2 separate subnets standing between a planned Wake-on-LAN flow and the machine it’s supposed to wake
  • 0 physical steps taken this session, and still a genuine near-miss caught before it shipped

Comments