Repository navigation
Portal silo fails to start — orleans clustering database host does not resolve (memex) #3780
Description
What is failing
During a roll of the memex control instance (AKS namespace memex), a fresh portal pod failed to boot: the Orleans silo's MembershipTableManager — cluster membership on the orleans PostgreSQL database reached via ConnectionStrings__orleans (AdoNet clustering) — aborted silo start at lifecycle stage RuntimeGrainServices because the database hostname did not resolve (SocketException: Name or service not known out of Dns.GetHostEntryOrAddressesCore; no TCP attempt was ever made). The portal then failed closed and the container exited. The same boot also surfaced as a second fingerprint at the generic Host log site (Admin/_LogIncident/1c9c655198118379) — that ticket is the same event and should be closed as a duplicate of whichever lands first.
Probable cause — medium confidence
The failure is name resolution of the orleans DB host, not connect/auth/timeout. Two candidates, and the evidence leans to the first:
- Transient resolver failure inside the boot window. The pod passed its
wait-for-postgresinitContainer (it reached the silo lifecycle at all), so a Postgres host resolved and accepted TCP seconds earlier. The portal's pre-start orleans gate (2026-08-17 What's New) re-reads theOrleansQueryrows from the database the silo will use before starting — if that gate ran on this build, the orleans host was reachable moments beforeMembershipTableManagerfailed. A host that is wrong in the connection string fails consistently; one that resolved seconds earlier and then does not points at a CoreDNS/resolver blip. - A wrong or default-derived
ConnectionStrings__orleanson this pod — the chart's documented trap class (DeploymentAKS → "Secrets the chart renders that no in-cluster Postgres backs", Memex#204): a templatedefaultcan manufacture a plausible URI pointing at a host that does not exist (an in-clustermemex-postgres-serviceis absent on AKS). The value renders intomemex-portal-secretsfrom the Key Vault values half (helm-values-memex) that no repository holds, and the instance record was re-applied two minutes before the failure — a re-render is inside the window.
One command distinguishes them: resolve the orleans host from the pod (below). Resolves now → cause 1; does not → cause 2.
Impact
A single failed boot on one pod of a two-replica deployment, no recurrence within the watch window — by the numbers a delayed roll, not an outage: the chart rolls surge-first (maxUnavailable: 0), so the previous ReplicaSet kept serving. The exposure is that memex is the control instance (infra control, accounting, home of the Deployments records): a persistent version of this stalls every roll of the instance, in the same "safe but stuck" shape as the documented DbVersionGate rollouts. Check the pod's restart count on the new ReplicaSet — still crash-looping means cause 2 and should be escalated.
Where to look
- From the pod:
getent hosts <orleans-host>— read the host out of the pod'sConnectionStrings__orleansby name/shape only (it is a secret); CoreDNS logs for the boot window. Deployments/memex— the instance record;ConnectionStrings__orleansarrives viavaultValuesKeysonmemex-portal-secrets(rendered fromhelm-values-memex, held in no repository); the record was last re-applied two minutes before the failure, pinning3.0.0-ci.8131.Doc/Architecture/DeploymentAKS— diagnose through the Hosting API (Sample/LogsonDeployments/memex) before break-glass kubectl; also documents the default-derived-URI trap class.Admin/_LogIncident/1c9c655198118379— same boot at theHostlog site; merge or close as duplicate.Admin/_LogIncident/2936e22a9e7f286b— earlier Npgsql incident but a different failure mode (connection timeout to a resolved in-cluster IP, Aug 23) — not a duplicate, same surface.
Evidence
| Fingerprint | 0b1877ae6c51353f |
| Category | Orleans.Runtime.SiloLifecycleSubject |
| Severity | Error |
| Namespace | memex |
| Pods | memex-portal-deployment-85b6cfc9c9-p5d7p |
| Occurrences | 2 |
| First seen | 2026-09-09 02:36:24Z |
| Last seen | 2026-09-09 02:36:24Z |
Recent log lines
2026-09-09 02:36:24Z memex-portal-deployment-85b6cfc9c9-p5d7p fail: Orleans.Runtime.SiloLifecycleSubject[100450]
'MembershipTableManager' failed to start due to errors at stage 'RuntimeGrainServices (8000)'.
Npgsql.NpgsqlException (0x80004005): Name or service not known
---> System.Net.Sockets.SocketException (00000005, 0xFFFDFFFF): Name or service not known
at System.Net.Dns.GetHostEntryOrAddressesCore(String hostName, Boolean justAddresses, AddressFamily addressFamily, Nullable`1 activityOrDefault)
at System.Net.Dns.<>c.<GetHostEntryOrAddressesCoreAsync>b__34_0(Object s, NameResolutionActivity activity)
at System.Net.Dns.<>c__DisplayClass40_0`1.<RunAsync>b__0(Task <p0>, Object <p1>)
at System.Threading.Tasks.ContinuationResultTaskFromTask`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Npgsql.Internal.NpgsqlConnector.ConnectAsync(NpgsqlTimeout timeout, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.ConnectAsync(NpgsqlTimeout timeout, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|209_0(NpgsqlConnector conn, String username, SslMode sslMode, GssEncryptionMode gssEncMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boo…[truncated]
2026-09-09 02:36:24Z memex-portal-deployment-85b6cfc9c9-p5d7p fail: Orleans.Runtime.SiloLifecycleSubject[100450]
Lifecycle start canceled due to errors at stage 'RuntimeGrainServices (8000)'.
Npgsql.NpgsqlException (0x80004005): Name or service not known
---> System.Net.Sockets.SocketException (00000005, 0xFFFDFFFF): Name or service not known
at System.Net.Dns.GetHostEntryOrAddressesCore(String hostName, Boolean justAddresses, AddressFamily addressFamily, Nullable`1 activityOrDefault)
at System.Net.Dns.<>c.<GetHostEntryOrAddressesCoreAsync>b__34_0(Object s, NameResolutionActivity activity)
at System.Net.Dns.<>c__DisplayClass40_0`1.<RunAsync>b__0(Task <p0>, Object <p1>)
at System.Threading.Tasks.ContinuationResultTaskFromTask`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Npgsql.Internal.NpgsqlConnector.ConnectAsync(NpgsqlTimeout timeout, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.ConnectAsync(NpgsqlTimeout timeout, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|209_0(NpgsqlConnector conn, String username, SslMode sslMode, GssEncryptionMode gssEncMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, Canc…[truncated]
Opened automatically from Admin/_LogIncident/0b1877ae6c51353f. Recurrences are folded into this issue rather than opening new ones.
Activity
rbuergi commentedon Sep 9, 2026
Consolidating five duplicate reports into this one
Six issues were filed for this single incident within 16 minutes by different sessions, all
describing the 02:35Z record-driven Reconcile of
memex(operator1a075bb, helm revision 44)whose new portal pod could not resolve its Postgres host. Closing the other five against this one so
the incident has one thread. Nothing here is a claim that the defect is fixed — see the status
at the end.
MembershipTableManageraborts silo start at lifecycle stageRuntimeGrainServices,SocketException: Name or service not knownonConnectionStrings__orleansDeployments/memex-reconcile-20260909-final)ceccd028…)The actionable statement, which #3781 and #3786 carry and this issue's own title does not: the
inner
SocketExceptionis the symptom; the defect is that a record-driven Reconcile rendered aPostgres hostname that does not resolve from inside the pod. Fixing the DNS name in one revision
does not close that — the question is why the record produced an unresolvable host.
Explicitly NOT consolidated: #3782 stays open on its own. It is a different defect that this
incident merely revealed —
helm upgrade --atomic --wait 15mrolled back an upgrade that hadalready landed, because the wait time-boxes instead of observing a gated rollout. That is a bug in
the deploy path independent of any hostname, and it would have bitten a healthy rollout too.
Status at the time of writing (2026-09-09):
https://memex.meshweaver.cloud/health,/aliveand/all answer 200, so the portal is serving. That is recovery, not evidence of a fix — therollback in #3786 is a sufficient explanation for a healthy portal, and this issue stays OPEN until
someone establishes why the rendered host did not resolve.
rbuergi commentedon Sep 10, 2026
Reopened — the 2026-09-10 sweep was not a triage decision
This issue was closed
not_plannedat 2026-09-10T14:30:36Z by a bulk sweep that PATCHed every open issue in all 13Systemorphrepositories — 311 issues in twelve minutes, no comment on any of them — after a session was asked to "close all issues in all repos". Nothing on this thread was read, and no judgement was made about this issue.All 82 issues the sweep closed in the MeshWeaver fleet were reviewed afterwards. This one is unresolved: Consolidated report for the 2026-09-09 silo boot failure (five duplicates folded in); no fix landed.
Sixteen of the 82 were left closed — their fix had actually landed, or the author had withdrawn them; those should have read
completed, notnot planned.rbuergi commentedon Sep 10, 2026
Cause 2 is EXCLUDED — measured through the Hosting API, no break-glass needed
The issue names its own discriminator:
That reading is available without resolving anything from inside a pod.
Deployments/memex-sample-20260910-1207-settled, a read-onlySampleonDeployments/memex, executed 2026-09-10T12:05:44→12:05:49Z:Two replicas ready, zero restarts, both on the same image. Not crash-looping.
The positive control: the operation that failed on 09-09 has since SUCCEEDED
Absence of a recurrence would be weak on its own — a negative that could mean "fixed", "not attempted" or "not observed". It is not the only evidence here.
Deployments/memex-restart-20260910-1140-activate-ai16restarted this same deployment on 2026-09-10 at 11:33:56Z, and its rollout is what the 12:05Z sample above reads back: both replicas replaced and Ready on3.0.0-ci.8238.So a fresh portal pod on
memexbooted through the OrleansMembershipTableManagerlifecycle stage, on the sameConnectionStrings__orleansrendered from the samememex-portal-secrets, twice.That is what excludes cause 2. A connection string naming a host that does not exist — the default-derived-URI trap class — fails consistently: every pod, every boot, from the same secret. These pods resolved it and started. Cause 1 (a transient resolver failure inside the boot window) is what survives, exactly as the issue's own reasoning predicted from the
wait-for-postgresinitContainer having passed seconds earlier.What that leaves, and it is not nothing
The cause is a CoreDNS/resolver blip outside this repository, so there is no code defect here to fix. What remains is a design question this repo does own, and I am not deciding it unilaterally:
Should a silo fail closed and exit the container on a transient NAME-RESOLUTION failure at boot?
The arguments are genuinely balanced, and the issue's own impact paragraph contains both:
DbVersionGatedoes, and a portal that starts without cluster membership is worse than one that does not start. The chart rolls surge-first (maxUnavailable: 0), so the previous ReplicaSet keeps serving and the cost of the refusal is a delayed roll, not an outage.DbVersionGaterollouts. A bounded retry at the resolution step specifically (not at connect, not at auth) would distinguish "the host does not exist" from "DNS did not answer this instant", which is precisely the distinctionSocketException: Name or service not knownwith no TCP attempt already makes visible.🚨 That is a maintainer call about failure posture, not a patch. I am leaving this issue open on that narrowed question rather than closing it on "did not recur" — a cause identified is not a decision taken, and closing here would lose the question.
Nothing was changed and no cluster command was run; every reading above came from
Hosting/InstanceActionrecords through the Hosting API.rbuergi commentedon Sep 13, 2026
Status 2026-09-13 — still the narrowed question, plus one chart fact worth having on the thread
Not recurred, and not a fix. Since the 09-09 boot both control-instance replicas have re-booted through
MembershipTableManagerseveral times on the sameConnectionStrings__orleans— most recently 2026-09-12 16:49:06Z and 16:51:56Z onc84c6c055(/healthbake-reporttimestamps), 2/2 ready. Cause 2 stays excluded as measured on 09-10; cause 1 (a resolver blip inside the boot window) stays the reading.One fact for whoever takes the posture decision. The init container that is supposed to make "the database is reachable before the portal starts" deterministic gates on one host:
deploy/helm/templates/memex-portal/deployment.yaml:104-111runsuntil nc -z $MEMEX_HOST $MEMEX_PORT, andMEMEX_HOSTis a config value. The orleans string is a secret (secrets.yaml:74-96): explicit (ConnectionStrings__orleans, "it always wins") or derived fromConnectionStrings__memex+Search Path=orleans. When it is explicit and names a different host fromMEMEX_HOST— a dedicated server, or the same server under another name — that host is gated by nothing; when it is derived,nc -zonMEMEX_HOSTonly covers it ifMEMEX_HOSTand the connection string'sHost=agree, which nothing checks. I could not read which shapememexuses (the values live in Key Vault), so this is a gap in the guard, not a claim about the incident.What is left is the same maintainer call as on 09-10 — fail-closed on a transient name-resolution failure at boot (today's posture; safe-but-stuck on the control instance) versus a bounded resolution retry that separates "no such host" from "DNS did not answer this instant". Not a code task until that is decided; leaving open on it. Read-only throughout.
rbuergi commentedon Sep 13, 2026
The chart fact from the 08:2xZ comment is now its own issue — #4173 — so it is not read as a claim about this incident:
wait-for-postgresprobesconfig.memex_portal.MEMEX_HOSTwhile the boot opensConnectionStrings__memex/__orleansfrom the secrets half, and nothing checks they name the same host. That hole is true whatever this incident's cause turns out to be; this issue stays on its own narrowed question (fail-closed vs a bounded retry at name resolution).rbuergi commentedon Sep 13, 2026
There is no posture decision left to make — the retry belongs in the init container, and that half is now fixed (#4219)
Re-measured today. Two separable questions were tangled here, and only one of them was ever a
posture call.
1. The half that was a real defect, and is fixable without deciding anything
wait-for-postgresprobed one host, taken fromconfig.memex_portal.MEMEX_HOST— a configvalue — while this boot opened
ConnectionStrings__orleans, a secret rendered from the KeyVault values half.
grep -rn MEMEX_HOST deploy/helm/templates/ scripts/returns four hits: the twoprobes and the two ConfigMaps that publish the value. Nothing compares them. That is #4173, and
it is exactly the shape that produces this fingerprint:
Init:1/1, the silo reachesRuntimeGrainServices,MembershipTableManagerfails on a name that never resolved, the containerexits.
🚨 The reading that cost the time here is the init container's PASS. A gate that was never
covering that host is indistinguishable, from the outside, from a resolver blip — which is why this
issue's own "probable cause" leans to the blip at medium confidence and needs a cluster command to
settle. A gate whose passing says nothing about the connection that failed is the same defect class
as a CI job that skips wearing a green tick.
#4219 closes it: both
wait-for-postgresinit containers now probe every distinct host:portthe process will open, derived from the connection strings themselves rather than from a parallel
config value, and they name each endpoint in the log as they wait. The strings move into one
templates/_database.tplso the portal Secret, the migration Secret and both probes cannot drift.A render that can derive no host now fails rather than emitting a probe that waits for nothing.
Two negative controls, both measured, in the PR: invariant 16 fails on two combinations without
the fix — including
two plain replicas, KEDA off, which is the memex.systemorph.com shape, soa combination this repo already shipped was un-gated — and every rendered Secret is byte-identical
before and after, so the refactor moved no connection string.
2. The posture question — it dissolves, it is not decided
Fail closed, or a bounded resolution retry in the silo? The question presupposes that the silo is
where the waiting has to happen. It is not, and once the gate covers the right host it is the wrong
place for it:
maxUnavailable: 0keeps the old ReplicaSet serving → a delayed rollA retry inside the silo buys the first row and pays for it in the second — and the first row is
already bought, for free, one layer down: the init container is the retry.
until nc -z …waitsas long as it takes, before the silo starts, and the pod shows
Init:0/1while it does. A blip nowcosts a slower pod rather than a crash loop; a wrong host now holds at
Init:0/1with the hostnamed in the pod's own log, instead of passing and dying on an error that names nothing.
So the silo's fail-closed stays correct because by the time it runs, every host it will open has
already resolved and accepted TCP. A failure there is then a real answer rather than a race — which
is what fail-closed is for. No timer, no watchdog, no bounded retry added to the silo. There is
nothing here for a maintainer to arbitrate.
Why this stays open for now
Whichever of this issue's two candidate causes it actually was, #4219 changes the failure mode:
Init:0/1naming the host, instead of passing and dying.But the cause was never established (the values live in Key Vault, which neither this issue's author
nor I can read), and it has not recurred, so there is no measurement that would confirm the
cure — only its absence, which proves nothing on a defect that occurred once. Closing it now would be
settling by silence. It should be closed after #4219 ships, on the next roll of this instance, if the
boot shape is what the change predicts.
Read-only on the deployment; nothing was changed on the cluster or the control instance.
rbuergi commentedon Sep 13, 2026
The guard half is merged (#4219,
16e18828) — leaving this open only for the confirmation a single un-recurred incident cannot give#4173is closed: bothwait-for-postgresinit containers now probe every distinct host theprocess will open, derived from the connection strings themselves rather than from
config.<half>.MEMEX_HOST, and they name each endpoint in the log as they wait. Thedeploy.shpath was fixed in the same change — it used to hand the external connection string over by patching
the Secrets after
helm upgrade, so a value arriving after the render could not reach the initcontainer the render produced; it now goes in through helm.
As set out above, there is no posture decision left: the retry lives in the init container,
where waiting is free and visible as
Init:0/1, and the silo's fail-closed stays correct becauseby the time it runs every host it will open has already resolved and accepted TCP. Nothing was added
to the silo — no timer, no watchdog, no bounded retry.
Why this is not being closed with it. The cause here was never established — the two candidates
(a resolver blip in the boot window vs. a
ConnectionStrings__orleansnaming a host that does notexist) are separated only by values held in Key Vault, and it has not recurred. An absence of
recurrence on a defect that occurred once proves nothing, so closing now would be settling by
silence. The next roll of this instance is the measurement: with the fix in, a blip costs a slower
pod and a wrong host holds at
Init:0/1naming the host — either is a different, diagnosableshape from
Init:1/1followed byMembershipTableManagerdying on an error that names nothing.Close it then.
rbuergi commentedon Sep 14, 2026
Measured on the incident node: it recurred 67 more times, it crash-looped, and this issue was never told
Read from
Admin/_LogIncident/0b1877ae6c51353fon memex.systemorph.com, 2026-09-14 ~06:1xZ.occurrencesfirstSeenlastSeenpods…-85b6cfc9c9-p5d7p,…-69999d86b8-8hsbs,…-5b967589bf-5j6g2)occurrencesAtLastComment1. The "transient resolver blip" reading is falsified for the 2026-09-12 episode
The issue rates cause 1 (a CoreDNS/resolver blip inside the boot window) above cause 2 (a wrong or
default-derived
ConnectionStrings__orleans), and offers a discriminator: "Check the pod's restartcount on the new ReplicaSet — still crash-looping means cause 2 and should be escalated."
The samples answer that without needing the cluster. Five bursts, all on one pod
(
memex-portal-deployment-5b967589bf-5j6g2), each aMembershipTableManagerfailure immediatelyfollowed by
Lifecycle start canceled:Five failed boots at a ~6-minute cadence on a single pod is the crash loop the issue names as
the escalation trigger. A transient resolver failure does not repeat on a fixed interval five times
against one pod while two sibling pods run. So for that episode the evidence points at cause 2,
not cause 1 — and the issue's stated confidence should be read the other way round.
2. It has not recurred since — so the bad pod is gone, not the cause established
Nothing since 2026-09-12T07:59:49Z, roughly 46 hours. The three pod hashes show the deployment
rolled through. So whatever carried the bad value went away with that ReplicaSet; that is a
disappearance, not a diagnosis, and it would return on the next re-render of the same values.
This is why I am not closing it. Closing on "no recurrence in 46 h" would repeat the #1391
mistake — a symptom's absence read as a cause's absence. What would actually close it is confirming
which of the two
ConnectionStrings__orleansshapes that pod received; per AGENTS.md that is aHosting-API read on
Deployments/memex, and the values half lives in Key Vault (helm-values-memex),which no repository holds — so it needs the maintainer, not an agent.
3. 🚨 The reason this issue still reads "a single failed boot, no recurrence"
occurrencesAtLastComment: 2againstoccurrences: 69. This issue stopped being updated after thesecond occurrence of sixty-nine. The incident node says why, in its own
errorfield:So the incident could not comment on its own ticket. Everything after occurrence 2 — including the
entire crash-loop episode that settles the cause — never reached this thread. The "every red log
gets exactly one ticket" guarantee held for filing and then silently stopped holding for
updating, which is the half that carries the evidence.
That is not specific to this incident and I am tracking it separately rather than here. Noting it on
this thread because it explains why the issue body's impact assessment ("a single failed boot … no
recurrence within the watch window") is not wrong so much as frozen at occurrence 2, and anyone
triaging from the body alone would rate this far lower than it deserves.
🤖 Generated with Claude Code
rbuergi commentedon Sep 14, 2026
The mechanism that froze this ticket is fixed — but the fix is roll-gated, so this thread stays manually current until then
Root cause of this issue's body reading "occurrence 2 of 69" was MeshWeaver.Plugins#1836: a log incident's comment debt was settled only by a new occurrence, so a fault that stopped left its ticket frozen at whatever count the last successful comment saw. Fixed in Plugins#1841, merged
7222fef934— a 15-minute reconcile over incidents whereOccurrences > OccurrencesAtLastCommentandCommentDueholds, on an injectable scheduler, with the 401 that created the debt deliberately not retried (it recovered on its own; the debt was the defect).It is not running anywhere yet. The reconcile lives in the
MeshWeaver.Observabilitymodule on the portal, and every portal is Held at627fb3cdpending a Roll (the delivery freeze — core #4293 + #4295 unfreeze the seal; the Roll is the maintainer's). Until a portal carries7222fef934, incident0b1877ae6c51353fkeeps its debt of 67 and this thread stays as I left it above — current by hand, not by the automation.When the roll lands, the observable that says it worked: this thread receives one automated comment reading roughly "Still occurring: 67 more since the last update (69 total)", and
occurrencesAtLastCommenton the incident node moves from 2 to 69. If that comment never arrives after the roll, #1836's fix did not reach the running module, and that is a new finding.🤖 Generated with Claude Code
systemorph-com commentedon Sep 14, 2026
Still occurring: 69 more since the last update (71 total).
memex-portal-deployment-85b6cfc9c9-p5d7p,memex-portal-deployment-69999d86b8-8hsbs,memex-portal-deployment-5b967589bf-5j6g2,memex-portal-deployment-77476b68-l4gw8Recent log lines
rbuergi commentedon Sep 14, 2026
The 2026-09-14 20:15Z recurrence, measured — and it is the same defect as 09-09, in code
Read-only throughout, through the Hosting API on memex.systemorph.com and the repositories; no cluster command, no InstanceAction written, the record untouched.
1. What rolled the control instance
Ops/Actions/reconcile-memex-20260914-nav-rail— a record-driven Reconcile (requested 20:13:57Z, operator imagehosting-operator:4c99ec2), whose step 4 "Re-apply the record" ran— one values file, logged as
10306 bytes, rendered from the record— producing helm revision 55 at 20:14:17Z. The surge podmemex-portal-deployment-77476b68-l4gw8failedMembershipTableManagerat 20:15:23Z and kept failing (incident node0b1877ae6c51353f: 85 occurrences by 20:30:36Z, all on that pod). On 09-09 it was revision 44, fromDeployments/memex-reconcile-20260909-final— also a Reconcile — and the portal recovered only because--atomicrolled it back onto the lane's revision 43.2. The rendered host vs the healthy pods' host
The record's render is secret-free by construction (
HelmValuesheader: "the Key Vault half is layered separately and this file never names a secret's value"). The chart'smemex-portal-secretsrenders fromsecrets.memex_portal.*, and formemexthose values live only in Key Vaulthelm-values-memex— the record's ownvaultValuesKeyslistsConnectionStrings__orleansamong the 11 keys that Secret carries. With nosecrets:block,memex.orleansConnectionStringindeploy/helm/templates/_database.tplfalls through to itsdefault:memex-postgres-serviceis the chart's in-cluster Service, rendered only underpostgres.enabled: true; every AKS namespace runsfalse. So the host in the new pod'sConnectionStrings__orleanswas a name that exists nowhere —SocketException: Name or service not knownfromDns.GetHostEntryOrAddressesCore, before any TCP attempt, exactly the fingerprint. The healthy pods were last rendered by the config repo'shelm-release.ymllane, which runshelm upgrade … -f vault-values.yaml -f <overlay>withvault-values.yaml=helm-values-memex, so their Secret carries the flexible server. Every Roll and Restart between the two incidents was healthy because neither touches the Secret — which is what made "did not recur" read as a resolver blip.Why
ConnectionStrings__memexdid not fail the same way: the record'skeyVaultSecretClasses[0](memex-kv, later inenvFrom) shadows it with the vault objectmemex-connectionstring. Nothing shadows the orleans string anywhere in the fleet. Why the init container passed: operator image4c99ec2predates #4219 (16e18828is not its ancestor), so its baked chart still probedconfig.MEMEX_HOST, which the record renders correctly (memexaks-pg.postgres.database.azure.com).The other session's remediation confirms it from the operational side:
Ops/Actions/helm-memex-20260914-deploy-8612-b— "Deploy memex through the helm-release lane — restore the Key Vault connection strings helm revision 55 dropped" — dispatched Memex run 34901699934 at 21:58Z.3. The code path, and the defect
deploy/aks/operator/bin/hosting-deploy(core, baked into the operator image):files=( -f "$values" )— it never read the vault half, whileHosting/Configuration.mddocuments that half as "layered FIRST at deploy". Two deploy paths, two answers to one question. Not the record, not Key Vault, not DNS: the operator path replaced layer 2 of the pod's environment with chart defaults on every record-driven deploy of an instance on an external database.The fix (root cause, both halves)
hosting-deploy --vault <name>layershelm-values-<release>FIRST, then the record's render (the lane's order; the record still wins on every key it declares); read by the operator's own identity, never echoed, size-only in the log; required where the flag is given (a vault holding none refuses before helm, naminghelm-release capture). And the chart refuses to invent the host:memex.orleansConnectionStringnowfails the render on an external database when neither connection string is supplied, instead of manufacturingmemex-postgres-service;check-chart-invariants.shcarries a REFUSALS control with a fixture of exactly this render, which must fail to template. Operator suite 464/464 (11 new; 9 red against the pre-fix script); chart harness 8 combinations + 1 refusal (the refusal reds against the pre-fix template).--vault <keyVault>on Reconcile and Provision exactly when the record declaresvaultValuesKeys; a record that declares the half but names nokeyVaultis refused by name; a provisioned instance (vaultValuesKeys: [], connection string on a CSI class) keeps its one-layer shape. Hosting 1.20.Outcome (2026-09-15 02:2xZ)
b63310a7(queue-merged 22:2xZ), MeshWeaver.Plugins#1892 →d9d314a5(Hosting 1.20, merged 22:41:48Z; the two new cases are registered in the Tests area, so they run rather than only compile).Hosting Operatorlane onb63310a7(run 34904032153) pushedmeshweaver.azurecr.io/hosting-operator:b63310a(+:main) and verified both tags in the registry.Ops/Actions/helm-memex-20260914-deploy-8612-bdispatched Memex run 34901699934 ("restore the Key Vault connection strings helm revision 55 dropped, and apply the committed pin 3.0.0-ci.8612"): itsUpgrade the releasestep succeeded at 22:2xZ with-f vault-values.yaml -f <overlay>; only the boundedObserve the rollout until it completesstep ran out — the rollout itself completed:https://memex.systemorph.com/api/versionanswersb8143398a7(set 8612's core commit) on every probe, and incident0b1877ae6c51353frecorded nothing after 21:59:25Z (125 occurrences total; the last ones on…-78d795bf59-87c7x, the rollback-to-8411 pod, which still carried revision 55's Secret — the same mechanism, not a third cause).That is the positive control the diagnosis predicted: the same image that crash-looped under the record-only render boots under the render that carries the vault half. Closing as completed.
Still owed, outside these two repositories (not done here — read-only on the record)
Deployments/memex→operator.image:hosting-operator:4c99ec2→hosting-operator:b63310a(Systemorph/Memex,mesh/Deployments/memex). Until that pin moves, a record-driven Reconcile ofmemexruns the old script; once Hosting 1.20 is live on the control instance the plan emits--vaultand the old image refuses withunknown argument '--vault'— loud, where it used to be a silent wrong render. Neither intermediate state can reproduce this incident.--vault.memexonhosting-operator:b63310amust showvault helm-values-memex (N bytes) — layered FIRSTin the operator job log and a new pod passingMembershipTableManager. A pod dying there again on that image is a new finding.Init:0/1namingmemex-postgres-service— a diagnosable stall, not a crash loop; the record path should feeddatabaseConnectionSecretintosecrets.<half>.ConnectionStrings__memexas well.systemorph-com commentedon Sep 17, 2026
Reopened — this fault is happening again.
Still occurring: 54 more since the last update (125 total).
memex-portal-deployment-85b6cfc9c9-p5d7p,memex-portal-deployment-69999d86b8-8hsbs,memex-portal-deployment-5b967589bf-5j6g2,memex-portal-deployment-77476b68-l4gw8,memex-portal-deployment-78d795bf59-87c7xRecent log lines
rbuergi commentedon Sep 17, 2026
Re-closed — the reopen above was on stale evidence (#4577).
The recurrence bot reopened this at
2026-09-17T07:51:32Zunder the headline "this fault is happening again", but the occurrence its own comment cites is OLDER than the close it reverted:Last seen:2026-09-14 21:59:25Z2026-09-15T02:25:11ZSo the counter moved, not the clock — the fault's newest occurrence predates the close, which is the one thing "still happening" has to mean. Root cause and fix:
LogIncidentFiler.Reopenreopened on the sole condition that the issue was closed, while the comment-debt clock pays a final count for incidents whose fault has stopped (the fix, Systemorph/MeshWeaver.Plugins#2014, is written but NOT yet merged — it makes a reopen requireincident.LastSeen > issue.ClosedAt).This restores the close exactly as it stood; nothing is asserted about the defect itself. If the fault happens again, the next occurrence postdates this close and reopens the issue on evidence that means what it says.
One of 53 issues whose reopen was measured stale (129 bot reopens on 2026-09-17, 125 of them inside the 07:51-07:53Z sweep; 76 postdate their close and were left open). Measured and re-closed 2026-09-17 by Claude Opus 5.