Operator-directed. The wg0 lifeline retired at the 2026-09-06 headscale cutover is
on no interface anywhere, so anything pointing at it gets no route at all. Homepage
went from 9 dead cards to 0 of 112.
The load-bearing part is that there is no single right target: it depends on who
resolves it. The operator's browser and the Homepage and open-webui containers on
esh-docker-vm all resolve nh3.internal, so those get the name and survive the next
renumber. Containers on irv-ml1 and ana-docker cannot resolve it at all, so those
get the IP.
litellm on ana-docker looked like a counterexample and is not: it resolves the name
only through its own extra_hosts entry, while asset-engine on the same host fails on
it. Test from the container you are about to change, never from a neighbour. Before
committing to the name I confirmed the Homepage container actually fetches ytvc's
healthz through it in production rather than assuming resolution implies reach.
On irv-ml1, 24 files swept and 14 comment-only hits left as port-allocation history.
Seven running containers recreated so the labels took. Seven dormant ones carried
stale labels because editing a compose file does not touch an existing container
object - fixed with compose create --force-recreate, which rebuilds the container
without starting it, the right tool for a deliberately dormant stack.
The sweep's real find was off irv-ml1 entirely: four live values on two other hosts,
silently dead for nine days and alerting nobody. Open WebUI's read-aloud TTS,
asset-engine's inference host, and two skaldsong TTS URLs. Both running services were
recreated and verified reaching their targets afterwards rather than merely carrying
the new string.
One self-inflicted outage worth recording: I recreated breeze-tts for a cosmetic
label change and took ext-tts down for its ~90s CUDA-graph warm-up, returning 500. I
caught it only because I had taken a baseline before touching it. A label-only edit
still costs a full model reload on a GPU container.
Stand up Open WebUI v0.11.0 parallel to lobe-chat (:3210) as an
operator-approved candidate replacement. Env-declarative config
(ENABLE_PERSISTENT_CONFIG=False = the deploy is the config source of
truth), model picker auto-tracks the LiteLLM gateway with no pins,
background task model pinned to summarizer, TTS routed direct at the
dots gateway (:8198).
Gates verified on the box: G1 (declarative config, both directions),
G2 (picker == live gateway roster, no hand-listing), G3 (models/sync
genuinely reconciles create+delete), G5 (task model pinned). G4 (TTS)
handed to tts-dev.
Credential posture (G6): fresh capped LiteLLM key open-webui-esh
(all-proxy-models, $50/1mo budget) rather than inheriting the uncapped
lobe-chat-esh key; signup locked off after admin creation. Secrets
vaulted under esh-docker-vm/open-webui-*.
Note: the API-key toggle env var is ENABLE_API_KEYS (plural); a var
only reaches the container if compose names it in environment:.
lobe-chat is untouched.