The power went out this morning. Everything came back. The server was up. The network was up. The dashboards loaded. No alarms anywhere.
Except the speakers did not work. Then the Apple TV was not there. Then I looked properly. I found 840 entities at unavailable. Sixty-eight of them were Sonos alone, plus the streaming boxes and the lighting system. A third of the house quietly stopped its existence, and nothing told me.
I saw this once before, in January, just after the migration to the new server. I fixed it then. It came back, because the fix had the wrong shape.
Two services, one port
Home Assistant discovers much of the house over mDNS. That is the protocol that lets devices announce themselves on a local network. Sonos, Apple TV, Roku, and a pile of other integrations depend on it. When Home Assistant runs with host networking, it binds the standard mDNS port directly.
The NAS operating system underneath also ships a discovery daemon. That daemon wants the same port, for .local hostname resolution. Two services want one port. The loser gets a startup error. The whole error reads like this:
OSError: [Errno 98] Address in use
That is it. That is the whole event.
The first process at the port wins. After a clean boot, the operating system’s own daemon is up long before a container starts. So Home Assistant loses the race. It logs one line into a file that nobody reads. Then it comes up healthy in every respect but one: it cannot find a third of the house.
That part deserves attention. The service is not down. The dashboard loads. Automations run. Nothing pages you, because from the outside the system looks fine.
The system only lost the ability to see one category of device. The entities from that discovery mechanism drift to unavailable, one by one, over the next minutes. There is no alert for “a discovery protocol did not bind.” At the moment of failure, nothing is broken yet. The failure becomes visible later, as an absence.
Why it comes back
The January fix was the obvious one. Stop the daemon. Disable the daemon. Restart the container. It worked immediately, and it held for months. Months are exactly the time you need to forget that you did it.
The problem: a disabled service is a piece of state. On an appliance OS, you do not own that state.
A NAS distribution is not a general-purpose Linux box with a web interface. It is a system that manages its own service configuration. An appliance OS is the HOA of computers. On every boot, it reconciles the services toward its own config. My disable was not a durable decision. It was a local edit to a thing the platform owns. On the next boot, the platform put the thing back.
Not from malice. Not even as a bug, really. The platform did the thing it was built to do.
So my mental model was wrong. I thought that I changed a setting. In fact, I won an argument once, with a system that reopens the argument on every start.
Two power cycles. Two identical outages. The second outage had to teach me the same lesson again. The first outage let me believe that a one-time change was a fix.
I lose the race on purpose
The durable answer is not a better config change. The durable answer accepts that the daemon will start, and kills it afterward.
A boot-time job now waits thirty seconds. Then it stops and disables the daemon. The delay is the trick, and the trick is counterintuitive enough to spell out. I do not try to beat the operating system to the port. I would lose that race. The platform’s own services start well before any job I schedule. A race between a user job and a system service gives a fix that works four times out of five.
So I concede the first race on purpose, and I win the rematch. The OS starts its daemon. The boot settles. Then my job takes the port away. The container’s own restart policy picks up the port on a retry that it already planned.
The shape of the fix applies past this one daemon: on a system that reconciles its own state, do not make a change — install a reconciler of your own. Treat each setting outside the appliance’s management model as reverted on every boot. The durable version of the decision is a job that runs and re-asserts it. It is not a setting that you flip and remember fondly.
The trade-off is real but small. With the daemon disabled, the NAS itself does not answer to .local names. I address it directly instead, and that costs me nothing. A hostname that I type twice a month is worth less than a third of the house.
There were more elegant options, and I rejected all of them. Option one: give Home Assistant its own network interface, so nothing collides. That is correct, more complex, and a bigger change than the problem deserves. Option two: point Home Assistant at the system daemon instead of its own. That is not supported. Option three: move Home Assistant to a separate machine. That solves the problem completely, and then I own another machine.
The weakest joint
I want this on the record, because it is the weakest joint in the whole arrangement.
The reconciler lives in a user-level scheduled job. The job lives on the same operating system that already overwrote my preferences twice. I fixed the daemon problem with a mechanism inside the blast radius of the daemon problem. A platform update or a profile reset could remove the fix, silently. I would find out the way I found out today: the music does not work.
So verification is now part of the procedure, not an afterthought. After each reboot: confirm that the daemon is inactive. Check the container’s log for that one error string. Spot-check that a few discovered devices are alive. Three commands. Thirty seconds.
That looks excessive until I remember the alternative: two outages that stayed invisible until a person noticed the music was gone.
The three checks now live beside the reboot procedure. I no longer write “fixed” when I mean “worked once.” The power will blink again, and future me will have something better than a belief to check.