A values key can be set, rendered, and still not read

Three different things can be true of a key in a third-party chart's values file, and they look identical from the outside — helm reports success for all three:

the key reaches the render the component reads it
Read yes yes
Dropped by the chart no no
Passed through but not a field yes no

Only the first is what you meant. The other two are inert configuration that reads as coverage, and the third is the one that defeats the obvious check.

What it cost

On 2026-09-09, loki-0 was drained with its node at 01:09Z and every namespace's log history in the cluster restarted from that moment (#3773). A query at 01:08Z returned both portal pods' Application is shutting down... lines at 01:05:01Z; the same query over the same window at 01:11Z returned nothing at all, and the pods no longer appeared in sum by (pod) (count_over_time({namespace="memex"}[70m])). The lines lost were the ones the incident under investigation (#3772) actually needed.

persistence.enabled: true was already set, and it was doing its job — it preserves flushed chunks and the index. What dies with the process is whatever the ingester still holds in memory, and that is covered by a different knob: the ingester write-ahead log, plus flush_on_shutdown, which turns a graceful drain into a flush rather than a loss.

Why "we set the key" was not good enough

The same values file already documented, in prose, a key that is set-able and inert:

Rendering with --set promtail.config.wal.enabled=true yields ZERO occurrences of "wal" in the output. Setting it anyway would be a silent no-op that reads like coverage.

That was measured by hand, once, and written down. Nothing re-checked it, and nothing was checking the Loki side either — so when the ingester WAL keys were added, "they arrive" was a belief. They do arrive; that was rendered and confirmed. But a belief that happens to be true is not a control, and the whole point of the incident is that the evidence went missing without anyone being told.

The trap: the obvious check is vacuous for the component that matters

The natural gate is render the chart and assert every <component>.config.<KEY> the values set appears in the component's rendered configuration. That is the right check for Promtail, whose chart builds its config from named fields and silently discards the rest — it catches promtail.config.wal exactly.

For Loki it cannot fail. loki-stack merges loki.config into its defaults verbatim, so anything you write arrives in the render intact — including a key that is not a field at all. Measured, with ingester: deliberately misspelled as ingestor::

render check     Verified: 5 config leaf/leaves … all reach the rendered configuration   exit 0
loki -verify-config   line 18: field ingestor not found in type loki.ConfigWrapper       exit 1

The first version of the gate shipped that render check alone. It passed the typo — it could not fail for the one component #3773 was about. A gate that cannot fail for its own subject is worse than no gate, because it reports a tick nobody re-examines.

What closes it is handing the rendered configuration to the component's own binary. Loki validates its config strictly and rejects unknown fields and unparseable values, so loki -verify-config answers the question the render never could.

The shape that works

deploy/aks/scripts/check-observability-values.sh runs both halves, on every pull request (Chart GateObservability values (rendered + binary-validated)):

values.observability.yaml ──▶ helm template (PINNED version) ──┬──▶ every config leaf present
                                                               │    and unaltered?      ← promtail
                                                               └──▶ loki -verify-config  ← loki

Three properties are load-bearing:

deploy/aks/scripts/test-observability-values.py is the checker's own control — nine cases with helm and docker stubbed, so it runs offline in the Chart invariants job. Its positive case exists so that a checker which failed everything could not pass it, and deleting either half of the real checker turns it red.

What is still not covered

The general rule

When a values key matters, the question is never "is it set" and rarely "does it reach the render". It is does the component read it — and for a chart that passes configuration through verbatim, only the component itself can answer.

Related: Chart Ownership and the Runner Pool · Chart Drift · Red-Log Watching & Ticketing · Measuring a Live Portal Read-Only

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.