2026-08-26 The Alert Fired and There Was No Crash
What I set out to do
Nothing. A SigNoz alert URL landed in the session with no question attached. It pointed at “Docker container egress broken”, the rule built yesterday in 2026-08-25 Alerting on Docker’s Guest-Network Break, which had never fired for real.
What I actually did
Confirmed it as a true positive, from inside a container. Both probe series
were firing, so the rule’s own label design already said “guest networking”, not
“DNS” and not “the host gateway alone”. By hand: the macOS host reached LiteLLM
on :4000 in 5 ms and the internet in 54 ms, while from inside open-webui
every connect returned 000 with an empty remote_ip in well under a
millisecond. Forcing -4 and -6 both failed, and so did the literal
192.168.65.254. Instant refusal, not a timeout. The container’s own side was
healthy throughout: /proc/net/route held a correct default via its bridge
gateway, DNS resolved fine, docker version answered instantly, and all 21
containers reported healthy.
Found a one-command localizer. Running the same curl from the VM’s own
network namespace (docker run --rm --net=host curlimages/curl ...) also
returned 000. That moves the break off per-container NAT and onto the VM to
macOS boundary, which no previous episode had actually established.
Contradicted the runbook on two points. There was no sailor crash:
com.docker.sailor had an unbroken 16h30m uptime, the VM never rebooted
(containers still read “Up 12/16 hours”), and the logs held zero panic,
semawakeup, pthread_create failed, or guest-connect ... rejected. And it
flapped: four separate outages across the day (03:53 to 06:51, 07:13 to 07:30,
10:49 to 11:53, 13:02 to 13:30 EDT), three of which cleared with no restart at
all.
Restarted and recovered. osascript -e 'quit app "Docker"' looked like a
complete no-op, with the backend still in pgrep sixty seconds later. It had in
fact moved the app to Status: stopping, which only docker desktop status
reveals, but it then sat there: the user had to quit Docker Desktop by hand from
the UI before the shutdown completed. docker desktop restart brought it back
from there. All 21 containers came back on their own about 11 seconds after the
VM returned, identical set, and hm switch then ran clean with tofu reporting 0
added, 0 changed, 0 destroyed. Egress verified over ten consecutive probes, and the alert
resolved both series at 13:30:17.
What was striking
The detector paid for itself inside 24 hours, and the label design did the diagnosis. Yesterday’s argument for splitting the rules was that the combination which arrives should be the diagnosis. That held: both probes firing together, with the failing URLs in the labels, narrowed it to guest networking before I ran a single command. The hand verification confirmed a conclusion the page had already reached.
The mechanism in the runbook turned out to be incidental, not necessary. Every prior write-up of this class describes a sailor panic followed by a VM self-restart, and the recovery advice was built on top of that story. Today had neither, and the symptom was identical. That is a good argument for keying diagnosis on the observable (instant refusal from inside a container, host healthy) rather than the presumed cause. The 08-25 loose end asking whether the occurrences share a root cause is now sharper: at least one of them has no crash to point at.
“Only a full restart fixes it” was wrong in a useful way. Three of today’s four outages cleared on their own within about 20 to 60 minutes. The restart is not the only exit, it is how you stop waiting. That also means the alert cannot tell a flap that will self-clear from a break that is stuck, which is a real gap in what the page communicates.
A small tell worth remembering. While broken, host.docker.internal
resolved to an IPv6 ULA (fdc4:f303:9324::254) despite Docker Desktop’s
defaultNetworkingMode being ipv4only, and came back as 192.168.65.254 after
recovery. Separately, Docker Desktop’s own host-side proxy was degraded in the
same window, with login.docker.com and desktop.docker.com CONNECTs each
taking about 90 seconds.
Loose ends
- Root cause still unknown, and now there is not even a crash to blame. Nothing identifies what triggers the transition in either direction.
- The rule fires the same way for a 20-minute flap and an indefinite break. Worth thinking about whether a duration or repeat-count signal belongs on the page.
- The notification never arrived, and the reason is structural. No ntfy push
landed for any of the four outages. Despite its name, the
local-placeholderchannel is a real webhook to the sink atathena.tail0cef90.ts.net, reached over Tailscale, so delivery has to leave a container and cross the exact path the alert exists to report on.httpproxy.logshows container-origin TCP to a tailnet address retrying through the outage window and getting nowhere. The detector is sound and its delivery hop is not: the rule fires correctly into a void. A host-side notifier polling SigNoz’s published port and pushing to ntfy over the host’s own network would survive this, since host to container port forwarding kept working throughout (the SigNoz UI was reachable in a browser the whole time).