Back to the rack

Bringing my homelab back after 36 days down

A power blip took out my NAS's storage array and, with it, most of my cluster. Nothing told me for five weeks. This is what broke, how I brought it back, and what I changed so it can't happen quietly again.

October 2026. Built with k0s, Flux, Unraid, Sealed Secrets, restic and Prometheus.

Summary

Around August 29 a power event rebooted my Unraid NAS and the four Raspberry Pis that run my Kubernetes cluster. The cluster came back. The NAS’s storage array did not: auto-start was off, and one of its two parity disks had failed. With no array there were no NFS shares, so every workload that stored data on the NAS sat waiting for volumes that would never mount: Nextcloud and its database, the media server, and more.

I found out on October 4, about 36 days later. Nothing had alerted me, because Alertmanager was routing every alert to a null receiver.

Duration ~36 days (late August to October 4)
Impact File sync, photos, media, and single sign-on down; password manager admin unreachable
Data loss None. Every volume was intact once the array started
Detected by Me, trying to use it

What actually broke

It looked like one outage. It was really six problems stacked up, most of them years of small shortcuts:

  1. The array didn’t auto-start after power loss. Auto-start was off, and parity disk 1 had been disabled with 28 grown defects.
  2. Volumes pointed at a path the NAS doesn’t export. Fifteen NFS PersistentVolumes used the cache-pool path instead of the user-share path. They would have failed on any remount, not just this one.
  3. Single sign-on had already been dead for about six months. Bitnami removed its public images, Keycloak’s image pull failed, and oauth2-proxy crash-looped behind it.
  4. An open-ended Helm chart range. Nextcloud’s chart was pinned to >= a version, so a reconcile pulled a chart two Nextcloud majors ahead. Nextcloud refused the skip, which was the safe outcome, but it didn’t start.
  5. latest image tags with IfNotPresent. Nodes kept running whatever latest meant when they first pulled it. My password manager was a year behind its clients and failed every login.
  6. No alerting. Prometheus was collecting everything. Alertmanager sent all of it to null.

Number 6 is the real root cause. Everything else is the kind of thing that breaks; not knowing for five weeks is the failure.

Recovery

I worked through it in one long session, backing up before every step that touched data.

Making it impossible to miss next time

Getting it running took a day. Making sure I’d hear about the next one took the rest of the week:

What I took from it

How I worked

I did the recovery with Claude Code as a pair. The useful part was setting it up to be safe on infrastructure I care about: scripted runbooks for status checks, deploys, and backups, and a pre-command hook that blocks the operations that can destroy data. That includes the bad array-start command, deleting namespaces or local volumes, and force-pushing the GitOps repo. When the hook blocks something, it has to ask me. That mix of automation and guardrails is the same thing I’d want from any tool with production access.

Next: A GitOps cluster on four Raspberry Pis. Or go back to the rack.