A few nights ago I moved Immich onto the NAS and ended that journal with a nagging thought: the NAS pool has no redundancy, so it's now the single thing my photos depend on. Tonight the NAS reminded me it was listening. I sat down to do something completely unrelated — finally put proper monitoring on the homelab — and the monitoring's first act was to tell me the NAS had quietly taken Immich down with it.
The thing I meant to build
I wanted Uptime Kuma running: a self-hosted status page that pings every service and tells me when something stops answering. Up to now my "monitoring" has been noticing things are broken when I try to use them, which is a terrible way to find out. The goal was simple visibility across the whole stack.
It went up cleanly as another Docker stack on the server, on its own port, joined to the monitoring network so it can reach the Prometheus and Grafana containers by name. Rather than click 30-odd monitors into the web UI by hand, I drove its API from a throwaway Python container and provisioned the lot in one script: the gateway and WAN, the media stack, the NAS, the public n5hq.me endpoints, the seedbox. By the end it was watching 33 services across seven groups.
The cameras were their own small puzzle. The five UniFi Protect cameras sit on an isolated VLAN that the server can't reach by design, so the first round of pings all failed. The clean fix was one tightly scoped firewall rule — allow only the server's IP through to the camera VLAN, nothing else — added through the UniFi API. After that, all five answered.
It caught a real outage on the first run
While I was still checking the new monitors, one of them was already red: photo.n5hq.me returning a 502. My first assumption was that I'd pointed the check at the wrong place. I hadn't. The immich_server container had exited hours earlier and was sitting there unhealthy. Uptime Kuma had been live for about five minutes and immediately earned its keep by surfacing an outage I had no idea was happening.
The container log was blunt about it: an EIO: i/o error trying to write into /data/upload, which is the NAS share. So Immich wasn't really the problem; its storage was.
Chasing the wrong layer for a while
This is where I spent more time than I'd like, looking at the right symptom from the wrong angle. The CIFS mount to the NAS had dropped into an I/O-error state. The systemd automount that's meant to mount it on demand had hit its retry limit and given up, so the path was falling through to an empty folder — which is exactly what the Immich container saw, and why it kept crash-looping.
I cleared the failed mount units, and the mount started failing outright with the connection getting dropped mid-negotiation. The NAS itself was up the whole time: it answered pings instantly, its web UI was fine, the SMB port was open. But every attempt to actually mount a share got cut off. That pattern — healthy box, dead file service — pointed at the NAS's SMB service rather than the network or my client. A toggle of the SMB service in the NAS UI brought it back, the mount came good, and Immich started cleanly.
Then a few hours later it died again, the same way. A fix that doesn't hold isn't a fix, so I stopped patching the symptom and went looking for the actual cause.
Into the NAS itself
I turned on SSH for the NAS and went straight for the things that would either convict or clear the hardware. First the disks, because the whole pool is RAID0 with no parity to fall back on — a single sick drive would explain everything. Both 8 TB drives came back PASSED, zero reallocated sectors, zero pending, zero read errors. The filesystem's own error counters were all zero too: no I/O errors, no corruption. The data was never in danger. That was a relief I didn't fully feel until I saw the numbers.
So if the storage was clean, what was Samba choking on? The Samba logs answered it. There was a cores directory full of crash dumps, and the service log showed smbd restarting over and over. The connection log spelled it out:
> smbXsrv_open_create: ... NT_STATUS_INTERNAL_DB_CORRUPTION > INTERNAL ERROR: Signal 11: Segmentation fault
smbd was reading one of its own internal databases, finding it corrupt, and crashing on it. That's the "server abruptly closed the connection" my client kept seeing — the server process was dying mid-handshake.
The detail that made everything click: that database lives in /run/samba, which is a RAM-backed filesystem. Restarting the SMB service does not clear it, so smbd just re-attached to the same corrupt file and crashed again. That's precisely why my earlier toggle never held and why it came back hours later. Only a full reboot wipes that folder — or deleting the file by hand while the service is stopped. The most likely trigger was an unclean power event; there'd been a batch of network gear reboot itself earlier in the day that fits a brief power blip.
Making it not happen the same way again
Two changes. First, the mount itself: I switched both NAS shares from the on-demand, fail-fast setup to a permanent, retry-on-blip mount. The old setup would error out the instant the NAS hiccuped and then leave a stale mount that the container clung to; the new one rides out a wobble and reconnects, and never swaps the mount out from under a running container. After a reboot cleared the corrupt database, everything came back healthy: Immich running, the photo endpoint returning 200, the mount writable.
Second, and the reason this matters going forward: the new status page now pings me on Discord the moment anything goes down or recovers. I already live in that Discord server, so the alerts land next to everything else. I wired the webhook up using the bot token that's already on the server, and it now watches all 33 services. Next time the NAS sulks, I'll know in a minute instead of finding out when I open the photo app.
The part that was just satisfying
With the fire out, I made the status page actually look like mine. I pulled the exact colours and font off the live n5hq.me site and themed the whole thing to match — the near-black violet background, the Outfit type, hairline cards in a tight grid, green heartbeats. It's published behind uptime.n5hq.me. The site, the alerts, and the dashboard now all speak the same visual language, which is a small thing that makes the whole setup feel like one project instead of a pile of separate tools.
What's next, and why
The honest gap tonight was that all of this only worked because I was at a keyboard. If the NAS had fallen over while I was away from home, I'd have been stuck reading a Discord alert I couldn't act on. So I spent the end of the session researching how to drive a full Claude Code session from Discord on my phone — turns out there's now a first-party way to do exactly that, plus a mobile remote-control option for approving the riskier commands. I've got a plan written and the server already meets the requirements. The idea is simple: get the alert and fix the thing from the same phone, from anywhere.
A few nights ago I wrote that the NAS having no redundancy was a thread to pull on. It pulled itself. The difference this time is that I wasn't guessing — I had logs, I had eyes on it, and I walked it all the way back to a corrupt file in RAM instead of stopping at "the disks must be dying." The disks are fine. The backup story still isn't where it needs to be, and that's still the real homework. But tonight the homelab told me when it broke, told me why, and is now set up to tell me faster next time. That's the whole point of the watchtower.